nabulke
nabulke

Reputation: 11255

How to load related entities in a fast way

I'd like to load a Test by id including all related TestRuns and all Measurements using DbContext/EntityFramework from a MySql database.

This is the database schema:

enter image description here

What I tried so far:

public class TestRepository : Repository<Test>, ITestRepository
{
    public IQueryable<Test> GetTestComplete(int id)
    {
      return DbSet.Where(t => t.Id == id)
                  .Include(t => t.TestRuns.Select(tr => tr.Measurements));
    }
}

Unfortunately this takes a very long time to complete (about one minute for 1 Test/1 Testrun/15000 Measurements). I tried to understand the generated SQL code by using a query profiler, but couldn't make sense of the huge monster SQL statement.

Can you think of a better (i.e. faster) way to load the data using DbContext?


Update

Another try, also resulting in a looong loading time:

public Test GetTestComplete(int id)
{
    Test test = DbSet.Find(id);
    DbContext.Entry(test).Collection(t => t.TestRuns).Load();
    foreach (var testRun in test.TestRuns)
    {
        // next line takes a lot of time!
        DbContext.Entry(testRun).Collection(tr=>tr.Measurements).Load(); 
    }
    return test;
}

Loading the measurements takes 84% of the time:

enter image description here

This is the corresponding sql statement for fetching the measurements:

SELECT 
Extent1.id,
Extent1.test_run_id,
Extent1.rss_dbm
FROM measurement AS Extent1
WHERE Extent1.test_run_id = :EntityKeyValue1

I copied each of the resulting sql statements (from the three DbContext/DbSet queries) from the query profiler into MySqlWorkbench, and each for itself is running very fast. Now I am even more confused...


Update 2

I isolated the function GetTestComplete (see above) in a single unit/performance test and it still takes a long time. The output of the query profiler shows that the individual sql commands are very fast, even though the whole test takes about 5 seconds to complete. Confusion growing...

enter image description here

Upvotes: 3

Views: 1892

Answers (2)

Gert Arnold
Gert Arnold

Reputation: 109079

Executing a query is one thing. EF will do that very fast. Quite another thing is materializing entity objects, creating DbEntityEntry and relationship objects for the change tracker.

If you fetch entities by...

DbSet.AsNoTracking()

...creating these DbEntityEntrys is taken out of the process, which usually considerably increases performance.

If you apply AsNoTracking, you can only use Include to load related entities. A statement like...

DbContext.Entry(testRun).Collection(tr => tr.Measurements).Load();

...will fail, because there won't be an entry for testRun in the first place, and the Load method is the opposite of AsNoTracking, sort of, because it is designed to load tracked entities into a context without returning them.

Upvotes: 2

Krzysztof Skowronek
Krzysztof Skowronek

Reputation: 2936

http://msdn.microsoft.com/pl-pl/library/bb738708%28v=vs.110%29.aspx - try this, but I have no idea about performance

Upvotes: -1

Related Questions