Один DbContext на запрос в ASP.NET MVC (без контейнера IOC) - PullRequest
49 голосов
/ 13 июня 2011

Извините, если на этот вопрос уже был дан ответ, но как вы гарантируете один DbContext Entity Framework на запрос, если вы не используете контейнер IOC?(Ответы, с которыми я до сих пор сталкивался, касаются контейнерных решений IOC.)

Кажется, что большинство решений подключаются к словарю HttpContext.Current.Items, но как вы гарантируете удаление DbContext по завершении запроса??(Или удаление с помощью EF DbContext? * Не является абсолютно необходимым?)

Редактировать

Я сейчас создаю экземпляр и утилизирую свой DbContext в контроллерах, но яу меня есть несколько отдельных экземпляров моего DbContext в ActionFilters и моего MembershipProvider (и я только что заметил, также пара валидаторов).Поэтому я подумал, что было бы неплохо централизовать создание и хранение моего DbContext для сокращения накладных расходов.

Ответы [ 6 ]

72 голосов
/ 14 апреля 2012

Я знаю, что это не недавний вопрос, но я все равно опубликую свой ответ, потому что я считаю, что кто-то может найти его полезным.

Как и многие другие, я следовал шагам, упомянутым в принятом ответе.Да, это работает. ОДНАКО , есть одна загвоздка:

Методы BeginRequest () и EndRequest () запускаются каждый раз, когда делается запрос , но не только для страниц aspx, но и для ВСЕХСТАТИЧЕСКИЙ КОНТЕНТ!Тем не менее, если вы используете код, упомянутый выше, и у вас есть на странице, скажем, 30 изображений, вы 30 раз восстанавливаете свой dbcontext!

Решением для этого является использование класса обертки для извлеченияконтекст, что-то вроде этого:

internal static class ContextPerRequest
{
      internal static DB1Entities Current
      {
          get
          {
              if (!HttpContext.Current.Items.Contains("myContext"))
              {
                  HttpContext.Current.Items.Add("myContext", new DB1Entities());
              }
              return HttpContext.Current.Items["myContext"] as DB1Entities;
          }
      }
 }

И затем для утилизации

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

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

Примечание: DB1Entities является производным от DbContext (генерируется VS).Вы, вероятно, захотите изменить его своим контекстным именем;)

Примечание 2: в этом примере я работаю только с одним dbcontext.Если вам нужно работать с несколькими, вам нужно будет изменить этот код в соответствии с вашими потребностями.Не принимайте это как какое-то окончательное решение мировых проблем, потому что это, конечно, не конечный продукт.Он предназначен только для того, чтобы дать подсказку, как это может быть достигнуто очень простым способом.

Примечание 3: Тот же подход можно использовать и в других ситуациях, например, когда выхотел бы поделиться экземпляром SqlConnection или любым другим ... Это решение не является эксклюзивным ни для объекта DbContext, ни для структуры Entity.

60 голосов
/ 13 июня 2011

Я бы использовал метод BeginRequest / EndRequest, это помогает убедиться, что ваш контекст удаляется должным образом, когда запрос закончен.

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}

И в вашем классе EntityContext ...

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}
10 голосов
/ 13 июня 2011

Одним из способов будет подписка на событие Application_BeginRequest, внедрение DbContext в текущий HttpContext и в Application_EndRequest выборку из HttpContext и удаление. Все, что находится между ними (и это почти все :-)) может извлечь DbContext из текущего HttpContext и использовать его. И, да, вы должны распоряжаться этим. И, кстати, есть ли причина, по которой вы не используете инфраструктуру DI, которая уже делает это для вас среди других полезных вещей?

7 голосов
/ 27 марта 2013

Небольшое дополнение для ответа Чад Морана. Это вдохновлено примечаниями Вальтера. Чтобы избежать инициализации контекста для статического содержимого, мы должны проверить текущий обработчик маршрута (этот пример только для MVC):

protected virtual void Application_BeginRequest()
{
  var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
  if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
  {
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
  }
}
1 голос
/ 13 июня 2011

Если вы реализуете IDisposable в своем контроллере и располагаете контекст в методе распоряжения, а также создаете новый контекст в конструкторе контроллера, вы должны быть в безопасности, так как экземпляр контроллера создается для каждого запроса.Я не понимаю, однако, почему вы хотите это сделать?... Вы должны использовать DI или создать фабрику контекста с одним статическим экземпляром контекста.Если вы не используете один экземпляр (вы создаете его для каждого запроса), у вас могут возникнуть проблемы в какой-то момент.Проблема с нераспределенным контекстом заключается в том, что EF кэширует данные в контексте, и если какой-то другой экземпляр контекста изменяет что-то в БД, которое уже кэшировано в другом контексте, - у вас непоследовательное состояние.До того, как DI стал настолько популярным, у меня был один статический экземпляр контекста где-то в приложении, и это намного быстрее и безопаснее, чем когда каждый запрос создает свой собственный контекст, но вам нужно реализовать код проверки состояния, который гарантирует, что этот контекстподключение к БД в порядке ... Есть гораздо лучшие решения этой проблемы, и лучше всего использовать некоторые DI Framework.Я бы порекомендовал Ninject в сочетании с MVCTurbine, его легко настроить, и вы можете добавить его через NuGet.

0 голосов
/ 14 июня 2011

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

...