2014-02-07 6 views
23

Есть что-то, что давно исказило меня в Entity Framework.Entity Framework, DBContext и использование() + async?

В прошлом году я написал большое приложение для клиента, использующего EF. И во время разработки все отлично работало.

Мы отправили систему в августе. Но через несколько недель я начал видеть странные утечки памяти на производственном сервере. Мой процесс ASP.NET MVC 4 занимал все ресурсы машины после нескольких дней работы (8 ГБ). Это было плохо. Я просматриваю в сети и вижу, что вы должны окружать все ваши запросы и операции EF в блоке using(), чтобы контекст мог быть удален.

В течение дня я реорганизовал весь свой код для использования using(), и это решило мои проблемы, так как тогда процесс сидит на устойчивом использовании памяти.

Причина, по которой я не окружала свои запросы, в первую очередь заключается в том, что я запустил свои первые контроллеры и репозитории из собственных лесов Microsoft, включенных в Visual Studio, они не окружали его запросы с использованием, вместо этого у него были DbContext как переменная экземпляра самого контроллера.

Прежде всего: если это действительно важно, чтобы избавиться от контекста (то, что не было бы странно, что dbconnection должен быть закрыт, и так далее), Microsoft, возможно, должны иметь это во всех своих примерах!

Теперь я начал работать над новым большим проектом со всеми моими знаниями в затылке, и я изучал новые возможности .NET 4.5 и EF 6 async и await. EF 6.0 имеет все эти асинхронные методы (например, SaveChangesAsync, ToListAsync и т. Д.).

public Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

В классе TblLanguageRepo:

public async Task<tblLanguage> Add(OrganizationTypeEnum requestOrganizationTypeEnum, tblLanguage language) 
{ 
    ... 
    await Context.SaveChangesAsync(); 
    return langaugeDb; 
} 

Однако, когда я теперь окружают мои заявления в using() блоке я получаю исключение, DbContext was disposed, до того, как запрос был в состоянии вернуться. Это ожидаемое поведение. Запрос выполняется async, и блок using завершен перед запросом. Но как я должен правильно распоряжаться своим контекстом при использовании async и ждать функций ef 6 ??

Пожалуйста, указывайте мне в правильном направлении.

Требуется ли using() в EF 6? Почему собственные примеры Microsoft никогда не показывают этого? Как вы используете асинхронные функции и правильно используете свой контекст?

+0

Это тесно связано: [Контекст данных EF - Async/Await & Multithreading] (http://stackoverflow.com/questions/20946677/ef-data-context-async-await-multithreading) – Noseratio

ответ

5

Я хотел бы использовать способ «один DbContext для запроса» и повторно использовать DbContext в запросе. Поскольку все задачи должны быть завершены в конце запроса в любом случае, вы можете безопасно утилизировать его снова.

См .: One DbContext per request in ASP.NET MVC (without IOC container) т.е.

Некоторые другие преимущества:

  • некоторые организации могут быть уже материализовались в DbContext от предыдущих запросов, сохраняя некоторые дополнительные запросы.
  • У вас нет всех этих дополнительных using заявлений, загромождающих ваш код.
1

I agree with @Dirk Boer что лучший способ управлять временем жизни DbContext - это контейнер IoC, который предоставляет контекст, когда завершается HTTP-запрос. Однако, если это не вариант, вы можете также сделать что-то вроде этого:

var dbContext = new MyDbContext(); 
var results = await dbContext.Set<MyEntity>.ToArrayAsync(); 
dbContext.Dispose(); 

using утверждение просто синтаксический сахар для размещения объекта в конце блока кода. Вы можете добиться такого же эффекта без блока using, просто позвонив .Dispose.

Давайте думать об этом, вы не должны получить объект, расположенным исключение, если вы используете await ключевое слово в блоке с помощью:

public async Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     var returnValue = langRepo.Add(RequestOrganizationTypeEnum, language); 
     await langRepo.SaveChangesAsync(); 
     return returnValue; 
    } 
} 
1

Если вы используете правильный п-уровневой скороговорку программирования, ваш контроллер должен даже не знаю, что запрос базы данных делается. Это должно произойти в вашем сервисе.

Существует несколько способов сделать это. Один из них состоит в том, чтобы создать 2 конструктора для каждого класса, который создает контекст и тот, который принимает уже существующий контекст. Таким образом, вы можете передать контекст, если вы уже находитесь на уровне службы, или создать новый, если это контроллер/модель, вызывающая сервисный уровень.

Другое - создать внутреннюю перегрузку каждого метода и принять там контекст.

Но, да, вы должны обертывать их при использовании.

Теоретически, сбор мусора ДОЛЖЕН очистить их, не обертывая их, но я не полностью доверяю GC.

0

ИМХО, это снова проблема, вызванная использованием ленивой загрузки. После того, как вы разместили свой контекст, вы больше не можете lazy-load свойство, потому что удаление контекста закрывает базовое соединение с сервером базы данных.

Если у вас есть отложенная загрузка активироваться и исключение происходит после using сферы, тогда смотрите https://stackoverflow.com/a/21406579/870604

24

Вашего кода:

public Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

избавляется хранилище перед возвращением Task. Если вы сделаете код async:

public async Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return await langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

тогда он будет располагать хранилище непосредственно перед Task завершается. Что на самом деле происходит, когда вы нажмете на await, метод возвращает неполное Task (обратите внимание, что блок using по-прежнему «активен» на этом этапе). Затем, когда задача langRepo.Add завершается, метод Post возобновляет выполнение и предоставляет langRepo. Когда метод Post завершен, возвращается Task.

Для получения дополнительной информации см. Мой async intro.

+0

@Steven, является ' Добавить 'ожидание? Я думал, что он просто вернул TEntity (предполагая, что langRepo - это DbSet) ... – danludwig

+6

@danludwig: «Добавить» в этом случае задается в вопросе op. Он ждет (и его действительно следует называть «AddAsync»). –

Смежные вопросы