Один DbContext для каждого запроса в ASP.NET MVC (без контейнера МОК)
Извините, если об этом уже ответили, но как вы гарантируете один Entity Framework DbContext для каждого запроса, если вы не используете контейнер IOC? (Ответы, которые я встречал до сих пор, касаются решений контейнеров IOC.)
Кажется, что большинство решений подключаются к словарю HttpContext.Current.Items
, но как вы гарантируете удаление DbContext, когда запрос завершен? (Или удаление не обязательно необходимо с помощью EF DbContext
?)
Edit
В настоящее время я создаю экземпляр и удаляю свой DbContext в своих контроллерах, но у меня также есть несколько отдельных экземпляров моего DbContext в ActionFilters и моем MembershipProvider (и я только что заметил, также пару валидаторов). Итак, я подумал, что было бы неплохо централизовать создание экземпляров и хранение моего DbContext, чтобы уменьшить накладные расходы.
Ответы
Ответ 1
Я бы использовал метод 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; }
}
}
Ответ 2
Я знаю, что это не недавний вопрос, но я все равно отправлю свой ответ, потому что считаю, что кто-то может найти его полезным.
Как и многие другие, я следовал шагам, указанным в принятом ответе. Да, это работает. ОДНАКО, там один catch:
Способы BeginRequest() и EndRequest() срабатывают каждый раз, когда запрос выполняется, но не только для страниц aspx, но и для ВСЕГО СТАТИЧЕСКОГО СОДЕРЖАНИЯ! Тем не менее, если вы используете код, упомянутый выше, и у вас на вашей странице есть 30 изображений, вы повторно создаете свой dbcontext 30 раз!
Решением для этого является использование класса-оболочки для извлечения контекста, что-то вроде этого:
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.
Ответ 3
Один из способов - подписаться на событие Application_BeginRequest
, вставить DbContext в текущий HttpContext и в выборку Application_EndRequest
из HttpContext и удалить. Все, что находится между ними (это почти все:-)), может извлечь DbContext из текущего HttpContext и использовать его. И, да, вы должны распоряжаться им. И, кстати, есть ли какая-то причина, по которой вы не используете рамки DI, которые уже делают это для вас среди других полезных вещей?
Ответ 4
Небольшое дополнение для ответа Чада Морана. Это вдохновлено примечаниями Вальтера. Чтобы избежать инициализации контекста для статического содержимого, мы должны проверить текущий обработчик маршрута (этот пример только для 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();
}
}
Ответ 5
Если вы реализуете IDisposable в своем контроллере и удаляете контекст в методе утилизации и создаете новый контекст в конструкторе контроллера, вы должны быть в безопасности, поскольку контроллер создается для каждого запроса.
Однако я не вижу, почему вы хотите это сделать?
...
Вы должны использовать DI или создать контекст factory с одним статическим экземпляром контекста. Если вы не используете один экземпляр (вы делаете по одному для каждого запроса), вы должны иметь проблемы в какой-то момент. Проблема с неразделенным контекстом заключается в том, что EF кэширует данные в контексте, и если какой-либо другой экземпляр контекста что-то изменяет в базе данных, которая уже кэшируется в другом контексте - у вас есть несогласованное состояние. До того, как DI стал настолько популярным, у меня был один статический экземпляр контекста где-то в приложении, и это намного быстрее и безопаснее, чем каждый запрос создает свой собственный контекст, но вам нужно реализовать код проверки состояния, который гарантирует, что этот контекст подключение к db в порядке... Есть много лучших решений этой проблемы, и лучше всего использовать некоторые рамки DI. Я бы порекомендовал Ninject в сочетании с MVCTurbine, его легко настроить, и вы можете добавить его через NuGet.
Ответ 6
Скользкий уклон здесь имеет несогласованное состояние. Если у вас есть приложение, у вас будет несколько пользователей, и у них есть потенциал для одновременного изменения данных, тогда вы можете столкнуться с проблемами с целостностью данных, если вы сохраняете один контекст.