开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜