使用 AsNoTracking() 进行实体框架延迟加载

Entity Framework lazy loading with AsNoTracking()(使用 AsNoTracking() 进行实体框架延迟加载)

本文介绍了使用 AsNoTracking() 进行实体框架延迟加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们目前正在为实体框架使用延迟加载并遇到内存不足异常.我们遇到这个异常的原因是因为 Linq 查询加载了大量数据,并且在后期它使用延迟加载来加载导航属性.但是因为我们不使用 NoTrackingChanges 实体框架缓存建立得非常快,这会导致内存不足错误.

我对 EF 的理解是,我们应该始终在查询中使用 NoTrackingChanges,除非您想从查询中更新返回的对象.

然后我使用 NoChangeTracking 进行测试:

var account = _dbcontext.Account.AsNoTracking().SingleOrDefault(m => m.id == 1);var contactName = account.Contact.Name

但我收到以下错误:

<块引用>

System.InvalidOperationException:当返回一个带有 NoTracking 合并选项的对象时,只有在 EntityCollection 或 EntityReference 不包含对象时才能调用 Load.

解决方案

您已指定 EF 不跟踪您实例化的 Account 值:

var account = _dbcontext.Account.AsNoTracking().SingleOrDefault(m=>m.id == 1);

因此,试图从它们中访问导航属性永远不会奏效:

var contactName = account.Contact.Name

您可以使用 Include() 显式包含所需的导航属性.所以以下应该工作:

var account = _dbcontext.Account.Include(a => a.Contact).AsNoTracking().SingleOrDefault(m=>m.id == 1);var contactName = account.Contact.Name;//没有异常,已经加载了

<块引用>

我真的不相信使用 AsNoTracking 会阻止使用延迟加载

可以很快测试:

DotNetFiddle 完整示例

public static void Main(){var actor1 = 新演员 { Id = 1, Name = "Vin Diesel" };var movie1 = new Movie { Id = 1, Title = "速度与激情", PrimaryActor = actor1 };使用 (var context = new MovieDb()){Console.WriteLine("========= 开始添加:movie1 ==============");context.Movies.Add(movie1);context.SaveChanges();Console.WriteLine("========= END 添加:movie1 ==============");var m1 = context.Movies.First();Console.WriteLine(m1.PrimaryActor.Name);var m2 = context.Movies.Include(m => m.PrimaryActor).AsNoTracking().First();Console.WriteLine(m2.PrimaryActor.Name);var m3 = context.Movies.AsNoTracking().First();Console.WriteLine(m3.PrimaryActor.Name);}}

输出:

<块引用>

========= 开始添加:movie1 ==============
========= END 添加:movie1 ===============
范·迪塞尔
范·迪塞尔
运行时异常(第 31 行):对象引用未设置为对象的实例.

变量m1被上下文跟踪,因此它可以延迟加载导航属性并打印值.m2 未被跟踪,但我已明确包含导航属性,因此它会打印该值.m3 没有被跟踪,我没有明确包含它,因此值为 null 并且我们得到一个 NRE.

We are currently using lazy loading for Entity Framework and running into out of memory exception. The reason why we're running into this exception is because the Linq query loads a lot of data and at latter stages it's using lazy loading to load navigation properties. But because we don't use NoTrackingChanges Entity Framework cache builds up really quickly which results in out of memory error.

My understanding with EF is the we should always use NoTrackingChanges on query unless you want to update the returned object from the query.

I then tested using NoChangeTracking:

var account = _dbcontext.Account
                        .AsNoTracking()
                        .SingleOrDefault(m => m.id == 1); 
var contactName = account.Contact.Name

but I get the following error:

System.InvalidOperationException: When an object is returned with a NoTracking merge option, Load can only be called when the EntityCollection or EntityReference does not contain objects.

解决方案

You've specified for EF to not track your instantiated Account value:

var account = _dbcontext.Account.AsNoTracking().SingleOrDefault(m=>m.id == 1);

Thus trying to access navigation properties off of them will never work:

var contactName = account.Contact.Name

You can explicitly include navigation properties you want by using the Include(). So the following should work:

var account = _dbcontext.Account
  .Include(a => a.Contact)
  .AsNoTracking()
  .SingleOrDefault(m=>m.id == 1);

var contactName = account.Contact.Name;  // no exception, it's already loaded

I'm really not convinced that using AsNoTracking prevents from using lazy loading

It can be tested really quickly:

DotNetFiddle Full Example

public static void Main()
{
    var actor1 = new Actor { Id = 1, Name = "Vin Diesel" }; 
    var movie1 = new Movie { Id = 1, Title = "Fast and Furious", PrimaryActor = actor1 };
    using (var context = new MovieDb())
    {

        Console.WriteLine("========= Start Add: movie1 ==============");
        context.Movies.Add(movie1);
        context.SaveChanges();
        Console.WriteLine("========= END Add: movie1 ==============");

        var m1 = context.Movies.First();
        Console.WriteLine(m1.PrimaryActor.Name);

        var m2 = context.Movies.Include(m => m.PrimaryActor).AsNoTracking().First();
        Console.WriteLine(m2.PrimaryActor.Name);

        var m3 = context.Movies.AsNoTracking().First();
        Console.WriteLine(m3.PrimaryActor.Name);
    }
}

Output:

========= Start Add: movie1 ==============
========= END Add: movie1 ==============
Vin Diesel
Vin Diesel
Run-time exception (line 31): Object reference not set to an instance of an object.

The variable m1 is tracked by the context, thus it can Lazy Load the navigation property and prints the value. m2 is not tracked, but I've explicitly included the navigation property so it prints the value. m3 is not tracked and I have not included it explicitly thus the value is null and we get a NRE.

这篇关于使用 AsNoTracking() 进行实体框架延迟加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:使用 AsNoTracking() 进行实体框架延迟加载

基础教程推荐