这不是"问题"的问题,而是"为什么会发生这种情况的问题
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();
我只想解释一下的行为
因为您说您的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);
sameObjects
是false
,这意味着Single
不会简单地返回对已经在内存中的ViewModel对象的引用,而是创建它们的新实例。
当您在第一个查询中应用ToList
时,ViewModels会立即具体化为ViewModels的内存集合,Single
只会返回对匹配但已存在对象的引用。sameObjects
将是true
。
因此,在没有ToList
的情况下,您正在为一个刚刚物化的对象设置IsSelected
属性,该对象不再引用,因此会立即在垃圾收集中消失。使用ToList
可以设置内存中集合内部唯一对象的属性。当您在视图中使用此集合时,标志仍然存在。