当我尝试使用.Include()加载时,EF Core是延迟加载

时间:2019-08-26 19:44:29

标签: c# entity-framework-core ef-core-2.2

我正在使用EF Core 2.2.6(首先是数据库),似乎仅启用了延迟加载使我无法急于加载。启用延迟加载是否可以排除使用任何容量的紧急加载?

namespace Example.Models
{
    public class Lead
    {
        public int Id { get; set; }
        public LeadOrganization LeadOrganization { get; set; }

        public Lead(ExampleContext.Data.Lead dbLead)
        {
            Id = dbLead.Id;
            LeadOrganization = new LeadOrganization(dbLead.LeadOrganization);
        }

        public static Lead GetLead(int id)
        {
            using (var db = new ExampleContext())
            {
                var dbLead = db.Leads
                    .Include(l => l.LeadOrganization)
                        .ThenInclude(lo => lo.LeadOrganizationAddresses)
                            .ThenInclude(loa => loa.AddressType)
                    .FirstOrDefault(l => l.Id== id);

                return new Lead(dbLead);
            }
        }
    }
}
namespace Example.Models
{
    public class LeadOrganization
    {
        public IEnumerable<LeadOrganizationAddress> Addresses { get; set; }

        public LeadOrganization(ExampleContext.Data.LeadOrganization dbLeadOrganization)
        {
            Addresses = dbLeadOrganization.LeadOrganizationAddresses.Select(loa => new LeadOrganizationAddress(loa));
        }
    }
}
namespace Example.Models
{
    public class LeadOrganizationAddress
    {
        public AddressType AddressType { get; set; }

        public LeadOrganizationAddress(ExampleContext.Data.LeadOrganizationAddress dbLeadOrganizationAddress)
        {
            AddressType = new AddressType(dbLeadOrganizationAddress.AddressType);
        }
    }
}
namespace Example.Models
{
    public class AddressType
    {
        public short Id { get; set; }

        public AddressType(ExampleContext.Data.AddressType dbAddressType)
        {
            Id = dbAddressType.Id;
        }
    }
}

ExampleContext.Data命名空间包含EF从数据库生成的部分类。 LeadLeadOrganizationLeadOrganizationAddressAddressType是类,它们在属性方面基本上是1:1的,带有部分的,但是添加了静态方法(是的,但这是我必须使用的。)

潜在客户有一个LeadOrganization,而后者又具有至少一个LeadOrganizationAddress,而后者又具有一个AddressType。

GetLead调用Lead构造函数时,即使应该急切加载查询中的数据。这会导致嵌套对象的问题。当它最终到达LeadOrganizationAddress构造函数时,DbContext已被处置,因此无法延迟加载关联的AddressType

我是否误解了渴望加载的全部内容?我认为它将在初始查询时检索所有数据,然后让我将其毫无问题地传递给构造函数。我不需要继续回到数据库并延迟加载任何内容。

如果启用了延迟加载,您可以简单地不急于加载吗?还有其他解决方法,例如强制其加载任何代理实体?

3 个答案:

答案 0 :(得分:2)

我假设您使用UseLazyLoadingProxies(),但想对查询中的特定包含项禁用延迟加载。尚未实现:

https://github.com/aspnet/EntityFrameworkCore/issues/10787

您现在唯一可以做的事情:

1。)禁用延迟加载代理(“所有属性的默认延迟加载”)

2。)然后对特定属性使用(手动实现)延迟加载,例如在您的一种情况下:

public class LeadOrganization
{
    private ILazyLoader _lazyLoader { get; set; }

    private IEnumerable<LeadOrganizationAddress> _addresses;

    public LeadOrganization(ILazyLoader lazyLoader)
    {
        _lazyLoader = lazyLoader;
    }

    public IEnumerable<LeadOrganizationAddress> Addresses
    {
        get => _addresses;
        set => _addresses = value;
    }

    public IEnumerable<LeadOrganizationAddress> AddressesLazy
    {
        get
        {
            _lazyLoader?.Load(this, ref _addresses);
        }
        set => this._addresses = value;
    }
}

因此,对于急切加载使用.Include(lo=>lo.Addresses),对于延迟加载使用.Include(lo=>lo.AddressesLazy)


编辑1

默认情况下,不应为所有属性启用IMO延迟加载-这可能会影响整个实现的性能。因此,在惰性加载为您带来优势的情况下,上述解决方案是一种替代方法。我还想在每个包含项中都使用此选项,例如.Include(o=>o.Addresses, LoadingBehaviour.Eager)-也许将来会存在。

答案 1 :(得分:1)

好,在调查了该问题之后,通过代理实现的EF Core 2.x延迟加载存在问题。相关的跟踪问题是

问题是导航属性 急于加载,但是LazyLoader不知道在处置导航属性时-无法安全地访问上下文更改跟踪器,而只是抛出异常。相关代码可以在第一行的here中看到:

if (_disposed)
{
    Logger.LazyLoadOnDisposedContextWarning(Context, entity, navigationName);
}

在我读到它时,它应该在带有以下“重大更改”-Lazy-loading proxies no longer assume navigation properties are fully loaded的EF Core 3.0中修复。它还部分解释了当前的问题:

  

旧行为

     

在EF Core 3.0之前,一旦放置DbContext,就无法知道从该上下文获得的实体上的给定导航属性是否已完全加载。

不幸的是,这对当前的问题没有帮助。我看到的选项是:

  1. 等待EF Core 3.0发布
  2. 不要通过代理使用延迟加载
  3. 关闭已处置上下文警告时的延迟加载-默认情况下为Throw,将其更改为LogIgnore,例如:

    optionsBuilder.ConfigureWarnings(warnings => warnings
        .Log(CoreEventId.LazyLoadOnDisposedContextWarning)
    );
    

答案 2 :(得分:0)

延迟加载并不是阻止属性实例化的原因,因为缺少适当的构造函数。

除了

EF Core,这些类型(例如LeadOrganization需要在其构造函数中的自身实例中传递。有点像鸡和鸡蛋的问题–您如何创建第一个?

public class LeadOrganization
{
    public IEnumerable<LeadOrganizationAddress> Addresses { get; set; }

    public LeadOrganization(ExampleContext.Data.LeadOrganization dbLeadOrganization)
    {
        Addresses = dbLeadOrganization.LeadOrganizationAddresses.Select(loa => new LeadOrganizationAddress(loa));
    }
}

无论如何,EF Core only supports simple constructors with parameters based on convention(基本上是参数到属性的1-1映射),因此它不知道如何实例化和混合那些渴望或懒惰的嵌套类。

我建议使这些构造函数成为无参数的,或者如果您希望EF通过属性来混合对象,则至少要将无参数的构造函数添加到类中。