从附加实体内部获取数据库上下文

时间:2019-07-16 23:41:39

标签: entity-framework

在n层应用程序中,我一直采用从存储库类返回分离的实体的方法。然后,我先进行手动附加,然后再进行更改。一切顺利,直到出现这种新情况为止……

我使用以下代码段从实体内部访问上下文:

    public abstract class EntityBase
{
    protected TheLeegzDbContext GetDbContext()
    {
        ObjectContext object_context = this.ObjectContext();

        if (object_context == null || object_context.TransactionHandler == null)
        {
            return null;
        }

        return (TheLeegzDbContext)object_context.TransactionHandler.DbContext;

    }
    private ObjectContext ObjectContext()
    {
        var field = this.GetType().GetField("_entityWrapper");

        if (field == null)
        {
            return null;
        }

        var wrapper = field.GetValue(this);
        var property = wrapper.GetType().GetProperty("Context");
        var context = (ObjectContext)property.GetValue(wrapper, null);

        return context;
    }
}

这似乎适用于尚未分离的对象。但是,当我通过.AsNoTracking()。FirstOrDefault()分离它,然后稍后再附加时,该行:

var field = this.GetType().GetField("_entityWrapper");

返回null。

总而言之,如果我不分离对象,则上一行会检索上下文,但是如果我分离然后重新附加对象,则上述行会失败(请注意:我将其附加到与检索对象相同的上下文中- -不确定这是否重要?!)。

之所以这样做,是因为在添加子实体(未急切加载)时,根聚合需要加载子实体以与“ ordinals”一起玩,以防新子被“插入”。所以,我想:

1)检索根对象并分离,然后...以后... 2)将根对象附加到上下文 3)调用“ AddChild”到根对象,(子对象包括属性“ Ordinal”) 4)让根对象使用GetDbContext()。Entry(this).Collection(e => e.Children).Load();加载现有的子对象。 5)如果新项的序数需要在现有子级的中间插入,则让根对象操纵现有子级对象以“移动”现有子级的序数。

例如

void AddChild(Child child)

if (this.Children == null)
    GetDbContext().Entry(this).Collection(e => e.Children).Load();

// Update ordinals of some children if new child ordinal requires inserting.

问题是。根的附加似乎没有提供字段“ _entityWrapper”,就像从数据库中检索并仍被跟踪一样。

我能做到这一点吗,或者如果我想插入一个新的孩子,是否必须急于加载现有的孩子?

1 个答案:

答案 0 :(得分:1)

您所确定的行为是设计使您拥有context.Configuration.ProxyCreationEnabled = false IF

在这种情况下,当您使用.AsNoTracking()加载数据时,返回的对象的类型应该是您的POCO数据类,否则EF将返回该类的代理版本,其中包括名为{{1}的其他字段}。

  

即使禁用了跟踪,您的逻辑约定也取决于_entityWrapper的可用性,因此您在加载数据之前应在上下文中设置_entityWrapper
  正如您的ProxyCreationEnabled = true类所期望的那样,您应该在上下文中编辑构造函数以将配置设置为强制代理,或者使用其他工厂方法在使用之前预初始化上下文。

     
    

将其放在EntityBase中是不合适的,因为必须在创建实体之前在上下文中对其进行配置。

  
EntityBase.GetDbContext()

当您将实体附加到上下文时,对象的类型实际上并没有神奇地变回代理类型,因此这解释了为什么在您的对象中没有称为{的 field {1}}在附加之前,它不可能在附加之后

要更改类型,将需要创建一个新类型的全新实例,并克隆所有属性值,您需要一个赋值运算符来执行此操作。尝试从创建的条目访问实体的事件不会提供作为代理类型的解析(尽管我认为应该是我第一次尝试使用它),因此以下重新分配不起作用,结果仍然是原始项目实例:

// Force _entityWrapper proxy generation for all queries
context.Configuration.ProxyCreationEnabled = true;
  

注意::当序数处理很重要,并且您尝试在C#中进行管理(在将子记录更新或插入到数据库中之前),然后在_entityWrapper,您永远不能百分百确定自己要添加到集合中的物品,尝试将所有物品加载到列表中是正确的,但是item = dbContext.Entry(item).Entity; 仅会引入列表中还没有的记录,您应该考虑刷新列表中 current 项的所有 ordinal 字段(如果有),在如果自从上次为当前上下文加载列表以来,任何并行操作都可能影响列表。


最后,即使启用了代理,如果您的N-Tier结构涉及将对象序列化并随后反序列化的层,则即使在序列化之前已跟踪对象,通常也不应在反序列化结果中完全获得代理。