Lazy vs eager loading performance on Entity Framework

我的梦境 提交于 2019-11-29 09:57:53

"What is the best practice here?"

The best practice is to

  1. set !application wide! performance target
  2. profile, benchmark, and locate bottle neck
  3. review and fine tune the bottle neck that gives you the greatest performance win for least work. (and from my experience 90% of the time it's not tsql)

Now that may seem a bit irrelevant, but from that point of view, which ever loading pattern you PROFILED to be optimal within your application domain is the correct way to go.

There's no "best practice" of eager/lazy. That's why both options are both available. Also if the tsql is your bottle neck and switching between eager/lazy still isn't hitting your performance target, you will need to go down a whole plethora of other tools such as query analyzer and query plan analyser in SSMS.


For some background:

I was googling "eager loading slow" and came here. Here's my result:

var foo = _context.Foos
    //.Include("Answers")
    //.Include("Attachments")
    .FirstOrDefault(q => q.Id == key);

Eager loading: 106ms

Lazy loading: 11ms + 5ms + 5ms

Lazy loading wins, end of story.

In addition to SQL statements that gives a huge results or lots of calls when using both eager and lazy there is huge job that takes place by putting and mapping into the ObjectContext/DbContext from the result. This causes a huge performance hit and I can't really recommend any of these when retrieving large amount of data.

The best solution is to specify an explicit Select call. However, it's a bit difficult to give you an example on how to do this without knowing how your viewmodel object is built up. So, what I do here is giving you an example that uses anonymous object's as result from the query.

This example gives you contacts with information about the customer the contact belongs to.

var contacts = context.Contacts.Where(row => row.CategoryId == 1)
                      .Select(row => new {
                                             ContactId = row.Id,
                                             Name = row.Name,
                                             CustomerName = row.Customer.Name
                                         }).ToList();

This query will generate an SQL SELECT that joins Contacts with Customer using an inner join, and then only select the Contact.Id, Contact.Name and Customer.Name columns.

This solution is far most the most effective way to retrieve data from server if you don't intend to work with the data and save the changes right back to the same context. It doesn't use either eager nor lazy loading.

If you could somehow query your solicitors table and filter the query using your already fetched list of applications then the fetched Entities would be cached in your context, which I believe will then be used for the navigation property instead of hitting the database.

I'm not sure exactly how to write the solicitors fetching query, but I was thinking something like this

int[] applicationIDs = applications.Select(x => x.ID).ToArray();
var solicitors = context.Solicitors.Where(x => x.Applications.Any(y => applicationIDs.Contains(y.ID))).ToArray(); // added toarray to cause execution cause im never sure when the LINQ actually runs

Have you considered to use sql view?

I don't quite sure about Sql Azure. However in sql server, you can have performance penalty when joining 2 tables without having proper indexes. Maybe this happen in your query.

To be noted, your before query is accessing 1 table with where clause, 2 calls. And in the after query, it is accessing 2 tables with where clause, 1 call. There is join in your after query and is likely to need different index.

You can create a sql view to make sure that a proper index is used. Then make your application call the view. Stored procedure can be used for this purpose too but it is less suitable for this.

Eager loading fetches redundant master data. It will take lots of memory, though object graph in context stores only single master data per entity, but SQL will dump lots of data in it's plate. I took following image from here

If you see, Data of User table also repeated as many as UserDetails table in result set of SQL query. That seem to differentiating factor in performance (In your case master columns has more records then detail table).

If performance is your major concern, I would recommend You to use LINQ join with same where clause while fetching data for detail table separately So in your case :-

step1

 var context = new MyContext();
    var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable);

and then step2

var solicitors = from s in context.Solicitors
join loanApp in context.LoanApplications
select s.columns
where loanApp. <<Same condition as in step 1 where clause>>

Thanks, your question made me to review my own code :-)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!