简体   繁体   中英

LINQ performing multiple queries instead of a single “joined” query

I am not a LINQ power user by any means, but can fumble my way around on a basic level. I have a question regarding how LINQ formulates it's query "strategy". I will try to explain this as best as I can and write an extremely dumbed-down example by memory.

I have a data model that contains multiple database views. Let's say the views have a column structure as follows:

PersonView

PersonViewId | Surname | GivenName | OtherViewId
------------------------------------------------

OtherView View

OtherViewId | PersonViewId | Name
---------------------------------

After setting the primary keys for the view (PersonView.PersonViewId / OtherView.OtherViewId) and setting the appropriate fields to be non-nulable, I create an association between PersonView.PersonViewId (Parent) to OtherView.PersonViewId (Child). I set it to be "one to one" and write some code to consume it:

StringBuilder s = new StringBuilder();
foreach(PersonView p in dc.PersonViews)
{
    s.AppendLine(p.OtherViews.Name + "<br />");
}

After noticing extremely poor performance, I profiled the database and noticed it was doing a query for each of the PersonView's in the foreach statement.

At this point I rewrote the query and replaced the association in the DBML with a JOIN in the LINQ query, profiled the database and it queried the DB as expected, only once.

I am thinking that it is something to do with then the DB is actually being queried, but am not sure where to be debugging that. Can someone point me in the proper direction on this to help me improve the performance of using associations or am I stuck using JOIN's to accomplish what I need?

Thanks :)

This is caused by lazy loading - you can get around that by applying LoadWith() (the equivalent of EF's Include() for Linq to SQL) and then doing your query afterwards:

var dlo = new DataLoadOptions();
dlo.LoadWith<PersonView>(p => p.OtherViews);
dc.LoadOptions = dlo;
//your query here

It's the lazy-loading (aka Deferred Loading) feature of LINQ-2-SQL at fault, when you do p.OtherViews.Name it's doing a query on your OtherViews table.

There's a couple of ways to deal with it, one is to turn-off Deferred Loading:

dc.DeferredLoadingEnabled = false;

Another is to make a projection of everything you want in your results, and use the projection:

var people = from p in dc.PersonViews
             select new {
                 Person = p,
                 Name = p.OtherViews.Name
             };

And then there's BrokenGlass's suggestion, which I didn't know about until now :-)

The reason you're seeing this is Linq uses deferred loading for those relationships. In other words, Linq won't load the association until you actually try and use it.

This can help performance because if you don't always need the association, you aren't performing the unnecessary JOIN, and you aren't retrieving data that you don't need.

It can hurt performance though if your certain you need the data, and it keeps making requests.

You can get around this by keeping your association as it was, but use DataLoadOptions. For example:

var dc = new DataContext();
dc.DeferredLoadingEnabled = false;

DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<PersonView>(o => o.OtherView);

dc.LoadOptions = loadoptions;

Now, any time you query a PersonView, it will automatically load the OtherView relationship.

The nice thing about this approach is you can turn it on and off when you need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM