【问题标题】:Get DbContext from Entity in Entity Framework Core从 Entity Framework Core 中的实体获取 DbContext
【发布时间】:2017-06-13 02:22:19
【问题描述】:
有没有办法获取一个实体被跟踪的 DbContext 实例(如果有的话)?
我为 EF6 找到了以下建议/解决方案
Get DbContext from Entity in Entity Framework
public static DbContext GetDbContextFromEntity(object entity)
{
var object_context = GetObjectContextFromEntity( entity );
if ( object_context == null )
return null;
return new DbContext( object_context, dbContextOwnsObjectContext: false );
}
private static ObjectContext GetObjectContextFromEntity(object entity)
{
var field = entity.GetType().GetField("_entityWrapper");
if ( field == null )
return null;
var wrapper = field.GetValue(entity);
var property = wrapper.GetType().GetProperty("Context");
var context = (ObjectContext)property.GetValue(wrapper, null);
return context;
}
有没有办法在 EF Core 中得到这个结果?
【问题讨论】:
标签:
c#
entity-framework
reflection
entity-framework-core
【解决方案1】:
没有。 EF Core 还没有延迟加载。如果它有,那么从它生成的代理最终将具有对加载它的 DbContext 的引用。到目前为止,还没有这样的参考。
【解决方案2】:
可以在创建时对实例/实体使用依赖注入。允许稍后从实体中检索拥有的 dbcontext。
例如
class Book
{
public readonly DBContext _dbcontext;
public Book(DBContext dbcontext)
{
_dbcontext = dbcontext;
}
}
【解决方案3】:
没有很好的方法来做到这一点。似乎没有简单的方法可以在构造实体对象之后但在调用代码中枚举之前将任何代码注入进程。
我考虑过对 InternalDbSet 进行子类化,但您只能修复对 .Find 方法的调用,并且 IQueryable 实现(您使用 DbSet 的主要方式)遥不可及。
所以我能看到的唯一选择是根本不允许访问 DbSet,但有访问器函数将为我设置 .Owner(或任何你想调用的)属性。这很麻烦,因为您通常必须为要创建的每种查询类型编写一个函数,并且调用者不能再使用 LINQ。但是我们可以使用泛型和回调来保留大部分的灵活性,尽管它看起来很丑。这是我想出的。
我正在移植和清理一个复杂的系统,所以我还不能真正测试这个,但这个概念是合理的。代码可能需要进一步调整才能按需要工作。这不应该有任何惩罚,例如在处理任何记录之前拉下整个表,只要您使用 EnumerateEntities 进行枚举,而不是 QueryEntities,但我还没有对此进行任何实际测试。
private void InitEntity(Entity entity) {
if (entity == null) {
return;
}
entity.Owner = this;
// Anything you want to happen goes here!
}
private DbSet<Entity> Entities { get; set; }
public IEnumerable<Entity> EnumerateEntities() {
foreach (Entity entity in this.Entities) {
this.InitEntity(entity);
yield return entity;
}
}
public IEnumerable<Entity> EnumerateEntities(Func<DbSet<Entity>, IEnumerable<Entity>> filter) {
IEnumerable<Entity> ret = filter(this.Entities);
foreach (Entity entity in ret) {
this.InitEntity(entity);
yield return entity;
}
}
public T QueryEntities<T>(Func<DbSet<Entity>, T> filter) {
if (filter is Func<DbSet<Entity>, Entity>) {
T ret = filter(this.Entities);
this.InitEntity(ret as Entity);
return ret;
}
if (filter is Func<DbSet<Entity>, IEnumerable<Entity>>) {
IEnumerable<Entity> ret = filter(this.Entities) as IEnumerable<Entity>;
// You should be using EnumerateEntities, this will prefetch all results!!! Can't be avoided, we can't mix yield and no yield in the same function.
return (T)ret.Select(x => {
this.InitEntity(x);
return x;
});
}
return filter(this.Entities);
}
public void QueryEntities(Action<DbSet<Entity>> filter) => filter(this.Entities);