【问题标题】:EF Core lazy loader behaviour with new entitiesEF Core 延迟加载器行为与新实体
【发布时间】:2022-01-04 20:20:19
【问题描述】:

lazy loading without proxies 上的 EF Core 文档引用了此示例:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts
    {
        get => LazyLoader.Load(this, ref _posts);
        set => _posts = value;
    }
}

所以我们有一个无参数构造函数和惰性加载器注入构造函数。文档然后说:

此方法不需要继承实体类型或 导航属性是虚拟的,并允许实体实例 附加到上下文后,使用 new to lazy-load 创建。

那么这对于新实例究竟意味着什么?由 EF 核心创建的实体将注入惰性加载程序,但 new 实体不会。这是否意味着一旦将新项目添加到上下文中,惰性加载器支持字段应该得到更新?

【问题讨论】:

    标签: c# entity-framework-core lazy-loading


    【解决方案1】:

    那么这对于新实例究竟意味着什么?由 EF 核心创建的实体将注入惰性加载程序,但不会注入新实体。这是否意味着一旦将新项目添加到上下文中,惰性加载器支持字段应该得到更新?

    这正是它的意思。请注意,附加到上下文这里

    允许使用 new 创建的实体实例在附加到上下文后延迟加载。

    表示跟踪(他们在大多数地方使用的常用术语),换句话说,将实体添加到上下文更改跟踪器的任何操作 - AttachAdd、@ 987654323@、Remove、将EntityEntry.State 设置为除EntityState.Detached、通过跟踪查询创建的实体等之外的任何内容。

    不幸的是,文档没有解释所有细节,因此以下是通过实验检索到的一些附加信息:

    首先,如上所述,惰性加载器服务属性是在跟踪实体时设置的。但反之亦然 - 当实体未被跟踪时,该属性将被清空,例如通过将 State 设置为 EntityState.Detached

    其次是对物业本身的实际要求。文档最后说:

    注意

    延迟加载委托的构造函数参数必须称为“lazyLoader”。计划在未来的版本中配置为使用与此不同的名称。

    这并不完全正确。

    实际要求是有

    • 属性与getter 和setter
    • 具有任何可见性(通常为private
    • 不区分大小写名称为“LazyLoader”
    • type ILazyLoaderAction&lt;object, string&gt;

    更新: OP 发现的其他要求:

    • 如果服务属性在基类中定义,getter 应该对派生类可见,即至少 protected。设置器可以是private

    • 该属性不能用[NotMapped]属性或Ignored流畅地装饰。

    同时,具有相应参数的“注入”构造函数是不需要的。创建实体实例时,EF Core 将使用任何构造函数并设置惰性加载器服务属性,其方式与跟踪现有实体实例时相同。

    【讨论】:

    • 我问这个问题是因为我在我们的应用程序代码中没有看到这种行为。跟踪新实体时,惰性加载器属性不会更新。如果我设置了一个简单的演示项目,那么我确实会看到记录在案的行为。显然,即使惰性加载器属性满足上述要求,我们的应用程序代码也有问题。
    • 是的,这很奇怪。我真的测试了所有这些场景,它们如前所述工作。唯一的其他要求是确保 dbContext.ChangeTracker.LazyLoadingEnabled = true; 是默认值。
    • 找到了我的问题(实际上是 2 个)。 1. 我的LazyLoader 属性在基类中定义,在private 可见性下找不到,它必须至少为protected(尽管setter 可以是private)。 2. 早期该物业是公开的,并被装饰有异常[NotMapped]。我们或许应该更新您的答案,以注意潜在的可见性问题。
    • @JonathanMitchell 太好了,很高兴你找到它!答案已更新,谢谢。
    【解决方案2】:

    当涉及到私有构造器和设置器时,EF 在某种程度上打破了封装规则。这不是一件坏事,因为将这些私有化的真正目标是确保对代码的访问井井有条,而无需猜测使用哪个和使用什么的混乱,但同时 EF 将通过反射忽略这些限制。

    当您将Attach 一个实体发送到DbContext 时,DbContext 将检查附加的实体是否有它可以注入的属性类型。这包括包括ILazyLoader,EF 将使用私有设置器进行连接。因此,您可以new 建立一个新实体,一旦附加到 DbContext,它就可以访问惰性加载器。

    【讨论】:

    • 当我创建一个新项目并将Add 放到上下文而不是Attach 时会发生什么?我是否应该期望在设置了惰性加载器的情况下最终得到一个处于未更改跟踪状态的实体?
    • 是的,添加新实体将被视为将实体与DbContext 关联,与附加相同,因此在添加ILazyLoader 后将可以访问。
    猜你喜欢
    • 1970-01-01
    • 2019-08-05
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多