在linq和ef中,更改值可以进行热切加载,但不能延迟加载

本文关键字:延迟加载 但不能 加载 ef linq | 更新日期: 2023-09-27 17:57:29

这不是"问题"的问题,而是"为什么会发生这种情况的问题

var chapters = story.Chapters.Select(
                    ch => new ChapterDisplayViewModel { 
                                   Id = ch.Id,
                                   Number = ch.Number});

首先我想得到一些数据。故事是一个类型为Story的实体类型,它与Chapter有一对多关系。我想更改我得到的章节集合中的一些数据,所以我写一些条件,如果是,请更改值

if(chapters.Any(c => c.Number == chapterNum))
   chapters.Where(c => c.Number == chapterNum).Single().IsSelected = true;

然后我将数据发送到视图,但问题是:

由于延迟加载,没有任何更改,即使在将数据发送到视图后,我所做的更改也没有触发,为什么?我做了一个赋值语句,不应该将数据传递给视图来触发它吗

解决方案当然是使用ToList来立即执行查询

var chapters = story.Chapters.Select(
                    ch => new ChapterDisplayViewModel { 
                                   Id = ch.Id,
                                   Number = ch.Number}).ToList();

我只想解释一下的行为

在linq和ef中,更改值可以进行热切加载,但不能延迟加载

因为您说您的Story类上有一个延迟加载的集合Chapters,所以我假设Chapters实际上是一个动态代理对象的集合。如果你用探查器查看数据库中发生了什么,你会看到这一行。。。

var chapters = story.Chapters.Select(
               ch => new ChapterDisplayViewModel { 
                               Id = ch.Id,
                               Number = ch.Number});

在数据库中执行查询所有Chapter对象的查询(到ChapterDisplayViewModel的投影不会在数据库中发生)。这是唯一的数据库查询。以下。。。

if (chapters.Any(c => c.Number == chapterNum))
    chapters.Where(c => c.Number == chapterNum).Single().IsSelected = true;

在存储器中对已经延迟加载的CCD_ 8的集合执行。投影发生在这一点上。

但这意味着Single操作符具体化了一个ChapterDisplayViewModel对象,这意味着:内部某个地方发生了new ChapterDisplayViewModel。对此进行简单检查:

var viewModel1 = chapters.Where(c => c.Number == chapterNum).Single();
var viewModel2 = chapters.Where(c => c.Number == chapterNum).Single();
bool sameObjects = object.ReferenceEquals(viewModel1, viewModel2);

sameObjectsfalse,这意味着Single不会简单地返回对已经在内存中的ViewModel对象的引用,而是创建它们的新实例。

当您在第一个查询中应用ToList时,ViewModels会立即具体化为ViewModels的内存集合,Single只会返回对匹配但已存在对象的引用。sameObjects将是true

因此,在没有ToList的情况下,您正在为一个刚刚物化的对象设置IsSelected属性,该对象不再引用,因此会立即在垃圾收集中消失。使用ToList可以设置内存中集合内部唯一对象的属性。当您在视图中使用此集合时,标志仍然存在。