Как быстро загрузить связанные объекты

Я хочу загрузить Test по идентификатору, включая все связанные TestRuns и все Measurements, используя DbContext/EntityFramework из базы данных MySql.

Это схема базы данных:

введите здесь описание изображения

Что я пробовал до сих пор:

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));
    }
}

К сожалению, это занимает очень много времени (около одной минуты для 1 теста/1 теста/15000 измерений). Я попытался понять сгенерированный код SQL с помощью профилировщика запросов, но не смог разобраться в этом огромном монструозном операторе SQL.

Можете ли вы придумать лучший (то есть более быстрый) способ загрузки данных с помощью DbContext?


Обновить

Еще одна попытка, также приводящая к долгому времени загрузки:

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;
}

Загрузка измерений занимает 84% времени:

введите здесь описание изображения

Это соответствующий оператор sql для получения измерений:

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

Я скопировал каждый из результирующих операторов sql (из трех запросов DbContext/DbSet) из профилировщика запросов в MySqlWorkbench, и каждый сам по себе работает очень быстро. Теперь я еще больше запутался...


Обновление 2

Я изолировал функцию GetTestComplete (см. выше) в одном тесте модуля/производительности, и это все еще занимает много времени. Выходные данные профилировщика запросов показывают, что отдельные команды sql выполняются очень быстро, несмотря на то, что весь тест занимает около 5 секунд. Растерянность растет...

введите здесь описание изображения


person nabulke    schedule 10.12.2014    source источник
comment
Если вы хотите только читать данные (а не изменять их), вы можете использовать DbSet.AsNoTracking(). Я думаю, что много времени уходит на менеджер состояния объекта и исправление отношений. Если вам нужно изменить данные, я думаю, вы должны попытаться получить только то, что вам нужно изменить.   -  person Gert Arnold    schedule 12.12.2014
comment
@GertArnold: Вы были правы: DbSet.AsNoTracking() сделает свое дело и сократит время запроса измерений с 4 с до 400 мс. Но как использовать AsNoTracking() с этим запросом DbContext.Entry(testRun).Collection(tr => tr.Measurements).Load();?   -  person nabulke    schedule 12.12.2014
comment
@GertArnold: О, и спасибо за ваш первый комментарий! Я с радостью приму это как ответ, если вы переместите его из комментария в ответ.   -  person nabulke    schedule 12.12.2014


Ответы (2)


Выполнение запроса — это одно. EF сделает это очень быстро. Совсем другое дело — материализовать объекты сущностей, создавать DbEntityEntry и объекты отношений для трекера изменений.

Если вы извлекаете сущности по...

DbSet.AsNoTracking()

... создание этих DbEntityEntry исключается из процесса, что обычно значительно увеличивает производительность.

Если вы примените AsNoTracking, вы сможете использовать только Include для загрузки связанных сущностей. Заявление вроде...

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

...потерпит неудачу, потому что в первую очередь не будет записи для testRun, и метод Load противоположен AsNoTracking, потому что он предназначен для загрузки отслеживаемые объекты в контекст, не возвращая их.

person Gert Arnold    schedule 12.12.2014
comment
Как запросить один тест по идентификатору, включая все тесты и все измерения? Я пробовал DbSet.AsNoTracking().Where(t => t.Id == id).Include(t => t.TestRuns.Select(tr => tr.Measurements)).SingleOrDefault(), но это вызовет ошибку EntityCommandExecutionException. - person nabulke; 12.12.2014
comment
Сообщение об исключении Lost connection to MySQL server during query - person nabulke; 12.12.2014
comment
OMG, это, вероятно, специфично для MySQL. Я никогда не видел, чтобы это происходило с сервером Sql. В Sql Server это, вероятно, предотвращается включением нескольких активных наборов результатов, но, насколько мне известно, в MySQL нет ничего подобного. - person Gert Arnold; 12.12.2014
comment
Большое спасибо за вашу помощь, Герт. - person nabulke; 12.12.2014

http://msdn.microsoft.com/pl-pl/library/bb738708%28v=vs.110%29.aspx — попробуйте, но я понятия не имею о производительности

person Krzysztof Skowronek    schedule 10.12.2014
comment
Это то, что я уже делаю в своем первом примере выше (т. е. использую Include). - person nabulke; 10.12.2014