2013-12-22 3 views
3

Я разрабатываю приложение с VS2013, используя EF6.02 и Web API 2. Я использую шаблон ASP.NET SPA и создаю RESTful api в отношении источника данных структуры сущности, поддерживаемого сервером sql. (В разработке это относится к локальному экземпляру SQL Server.)Asp.net web api + entity framework: несколько запросов вызывают конфликт данных

У меня есть два метода API до сих пор (тот, который просто считывает данные, данные, которые записывают данные), и я тестирую их, вызывая их в javascript. Когда я использую только один метод в своем сценарии, один работает отлично. Но если я вызываю оба в сценарии (не дожидаясь, чтобы обратный вызов был вызван), я получаю плохие результаты и разные исключения в отладчике. В некоторых исключениях указано, что сохранение не может быть выполнено, так как есть ожидающие транзакции. В другом исключении говорилось о конфликте с другими потоками. И иногда операция чтения с ошибкой нулевого указателя прерывается при попытке чтения набора результатов.

«Новая транзакция не допускается, поскольку в сеансе есть другие потоки».

Это заставляет меня задаться вопросом, правильно ли я получаю новый DBC-текст для запроса. Мой код для этого выглядит так:

static Startup() 
    { 
     context = new Data.SqlServer.AppDbContext(); 
    ... 
    } 

, а затем всякий раз, когда вы создаете экземпляр единицы работы, я обращаюсь к Startup.context.

Я попытался реализовать единицу рабочего шаблона, и каждый запрос имеет общий объект UOW, который имеет один объект DBContext.

Мой вопрос: есть ли у меня дополнительная ответственность за то, чтобы веб-запросы «играли красиво» с афоризкой? Я надеюсь, что это проблема, с которой другие уже справились. Возможно, ошибки, которые я вижу, являются законными в том смысле, что если данные одного пользователя касаются, они временно находятся в недопустимом состоянии, и если в этот момент приходят другие запросы, они действительно потерпят неудачу (и я должен кодировать ожидающие эти сбои). Я полагаю, что даже если каждый запрос имеет свой собственный DBC-контекст, они по-прежнему используют один и тот же базовый источник данных SQL, поэтому, возможно, это вызывает проблемы.

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

Спасибо за любую помощь или предложения ... -Бен

ответ

2

Ваша проблема заключается в том, где вы настраиваете свой контекст. Метод Startup предназначен для начала всего приложения, поэтому любой сделанный запрос будет использовать один и тот же контекст. Это не для каждой настройки запроса, а для каждой установки приложения. Что касается того, почему вы получаете ошибки, EntityFramework - NOT поточно-безопасный. Поскольку IIS создает много потоков для обработки параллельного запроса, ваш единственный context используется для нескольких потоков.

Что касается решения, вы можете посмотреть в

-Dependency рамки для инъекций (например, Ninject или Unity)

-местных с использованием оператора в классах UnitOfWork

using(var context = new Data.SqlServer.AppDbContext()){//do stuff} 

-Ил , Я видел экземпляры людей, создающих класс, который получает контекст для этого запроса и сохраняет его в HttpContext.Cache [] (используя уникальное имя, чтобы вы могли легко получить его в другом классе), что делает его возможным повторное использование одного и того же контекста для одного и того же запроса. Что-то вроде этого:

public AppDbContext GetDbContext() 
    { 
     var httpContext = HttpContext.Current; 
     if (httpContext == null) return new AppDbContext(); 
     const string contextTypeKey = "AppDbContext"; 
     if (httpContext.Items[contextTypeKey] == null) 
     { 
      httpContext.Items.Add(contextTypeKey, new AppDbContext()); 
     } 
     return httpContext.Items[contextTypeKey] as AppDbContext; 
    } 

Чтобы использовать вышеупомянутый метод, сделать простой вызов var context = GetDbContext();

Примечание

У нас есть все из перечисленных выше способов, но это конкретно к третьему способу. Кажется, это хорошо работает с двумя оговорками. Во-первых, не используйте это в операторе using, поскольку он не будет доступен для каких-либо других классов во время действия запроса (вы его уничтожаете). А во-вторых, убедитесь, что у вас есть звонок по адресу Application_EndRequest, который фактически распоряжается им. Мы видели, как эти маленькие педерасты висели вокруг после того, как запрос закончился в памяти, что вызвало огромный всплеск в использовании памяти.

+0

Большое спасибо за подробный ответ, Томми. Пора мне засучить рукава! – BenjiFB

+0

У меня была другая мысль: вместо выражения 'using' было бы целесообразно создать экземпляр контекста данных внутри конструктора контроллера? – BenjiFB

+1

@BenjiFB - конечно, вот где вариант один входит в игру. Вы можете использовать Ninject/Unity для инъекции контекста в ваш контроллер/блок рабочих классов и т. Д. Их также можно настроить как один раз для каждого запроса, чтобы каждый раз, когда вы это выбираете, вы не вводите новую копию. – Tommy

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