【问题标题】:Get Database Context from inside Attached Entity从附加实体内部获取数据库上下文
【发布时间】:2019-07-18 01:26:28
【问题描述】:

在 n 层应用程序中,我一直采用从存储库类返回分离实体的方法。然后我在进行更改之前手动附加。在这个新场景之前一切都很好......

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

    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");

返回空值。

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

我这样做是因为当添加一个子实体(不是急切加载的)时,根聚合需要加载子实体以使用“序数”,以防新的子实体被“插入”。所以,我想:

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.

问题是根的 .Attach 似乎没有提供字段“_entityWrapper”,因为它刚刚从数据库中检索并仍在被跟踪。

我可以做到这一点,或者如果我想插入一个新的孩子,我是否有必要急切地加载现有的孩子?

【问题讨论】:

  • 我明白你在做什么,这是延迟急切加载的一个有趣实现,它不像是延迟加载......你是否考虑过简单地添加一个重载来添加允许你通过-在上下文中?
  • 谢谢,克里斯。当然是一个选项,但“序数处理”是数据层本身的一种不变行为,所以有点希望域层不必知道这些事情。当然,域层应该知道它需要将实体根附加到上下文,以便可以跟踪更改,但是一旦完成,该根实体上的“添加子项”的实现应该被封装,而域不知道传递 DBContext。我认为“附加”会使实体回到一种状态,就好像它从一开始就从未分离过一样。显然不是。

标签: entity-framework


【解决方案1】:

您确定的行为是设计使然如果您有context.Configuration.ProxyCreationEnabled = false

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

您的逻辑约定取决于 _entityWrapper 即使在禁用跟踪时也可用,因此您应该在加载数据之前在上下文中设置 ProxyCreationEnabled = true
由于您的 EntityBase 类需要此功能,您应该在上下文中编辑构造函数以将配置设置为强制代理,或者在使用之前使用其他一些工厂方法来预初始化上下文。

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

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

当您将实体附加回上下文时,您的对象类型实际上并没有神奇地变回代理类型,因此这就解释了为什么如果您的对象中没有名为 @ 的 字段 987654329@在附加之前,不可能存在附加之后。

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

item = dbContext.Entry(item).Entity;

注意:当序数处理很重要并且您尝试在 C# 中管理它时(在更新或将子记录插入数据库之前)然后在Add,您永远无法 100% 确定您要添加的集合中已有的项目,您尝试将所有项目加载到列表中是正确的,但 .Load() 只会引入以下记录尚未在列表中,您应该考虑刷新列表中 current 项的所有 ordinal 字段(如果有的话),以防有任何并行操作自上次为当前上下文加载以来,可能已经影响了列表。


最后,即使启用了代理,如果您的 N 层结构涉及序列化对象并随后反序列化它们的层,则通常在反序列化结果中根本无法获得代理,即使在序列化之前跟踪对象也是如此.

【讨论】:

    猜你喜欢
    • 2010-09-06
    • 1970-01-01
    • 2016-03-05
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多