Changing value works with eager loading but not lazing loading in linq and ef
thi开发者_JAVA百科s is not 'a problem' question , but 'why this happens' question.
var chapters = story.Chapters.Select(
ch => new ChapterDisplayViewModel {
Id = ch.Id,
Number = ch.Number});
first i want to get some data. story is an entity type of type Story
and it has One To Many relation with Chapter
i want to change some data in the chapters collection that i got so i write some condition if so change the value
if(chapters.Any(c => c.Number == chapterNum))
chapters.Where(c => c.Number == chapterNum).Single().IsSelected = true;
and then i send the data to the view but the problem is :
nothing is changed due to lazy loading , the change that i made wasn't triggerd even after sending the data to the view , Why ? i made an assigment statment and shouldn't passing the data to the view trigger it ?
the solution was of course using ToList to execute the query immediately
var chapters = story.Chapters.Select(
ch => new ChapterDisplayViewModel {
Id = ch.Id,
Number = ch.Number}).ToList();
i just want an explanation to the behavior
Because you say that you have a lazily loaded collection Chapters
on your Story
class I assume that Chapters
is actually a collection of dynamic proxy objects. If you look what happens in the database with a profiler you will see that this line ...
var chapters = story.Chapters.Select(
ch => new ChapterDisplayViewModel {
Id = ch.Id,
Number = ch.Number});
... executes a query in the database which queries all Chapter
objects (the projection into ChapterDisplayViewModel
doesn't happen in the database). And that's the only database query. The following ...
if (chapters.Any(c => c.Number == chapterNum))
chapters.Where(c => c.Number == chapterNum).Single().IsSelected = true;
... is executed in memory on the already lazily loaded collection of Chapters
. The projection happens at this point.
But this means that the Single
operator materializes a ChapterDisplayViewModel
object, it means: There happens a new ChapterDisplayViewModel
somewhere internally. Simple check for this:
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
is false
which means Single
doesn't simply return references to ViewModel objects which are already in memory but creates new instances of them.
When you apply ToList
in the first query the ViewModels are materialized at once into a in-memory collection of ViewModels and Single
will simply return a reference to the matching, but already existing object. sameObjects
will be true
.
So, without ToList
you are setting the IsSelected
property on a just materialized object which you don't reference anymore and therefore disappears in garbage collection immediately. With ToList
you are setting the property on the unique object inside of the in-memory collection. When you use this collection in your view the flag is still there.
精彩评论