Написанный на .NET 4.8, NancyFX 1.x и EF 6.0 проект.
В роли базы данный — MSSQL.
Все API (NancyFX), DAL и EF6 вызовы асинхронны (async+await).
Dependency Injection для DbContext — Request Singleton, т.к. активно используются транзакции.
Всё хостится в IIS, Application Poll — integrated.
Всё работает как часики уже три года и под большой нагрузкой с кучей активных пользователей.
Потребовалось:
Портировать всё на .NET Core.
В связи с тем, что время еще есть, а .NET 5.0 уже на подходе, приняли решение портировать таким образом: .NET 4.8 => .NET 5.0
.EF6 => EF6
NancyFX => WebAPI
TinyIoCContainer => Microsoft.Extensions.DependencyInjection
Всё хостится в IIS в in-process конфигурации (Application Poll — integrated).
Всё вроде портировалось, но начались ...
Проблемы:
Если при Reqest-е к WebAPI в EF один единственный async запрос (linq to entities), то всё нормально. Даже если парочка в цепочке, то тоже отрабатывает.
Но если же там цепочка async вызовов, то где-то в середине цепочки (точное место от вызова к вызову "плавает"), linq to entities запросы вылетают с exception-ом, в котором говорится, что к этому моменту DbContext уже в состоянии disposed.
Эксперимента ради пробовали перевести запросы в синхронное выполнение — ошибки пропадают, на нам надо асинхронное.
Добавив к DbContext уникальный идентификатор (создаем GUID в конструкторе), и выводя его в лог при вызове конструктора и при dispose видим, что:
— DbContext действительно Request Singleton
— Log-сообщение из dispose появляется после возникновения, описанного выше, exception-а.
Мы даже не знаем на что грешить — на Dependency Injector, на .NET Core (.NET 5.0), на хостинг в IIS, или еще на что!
Кто-то сталкивался с такой проблемой?
не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,
lazy loading используется? мне кажется, что подобные проблемы именно с этим связаны...
вообще, связка странная: адекватнее было бы имхо: .net core 3.1 + ef core 3.1
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Здравствуйте, B7_Ruslan, Вы писали:
B_R>Образец кода желательно показать. Запросы только на чтение?
Запросы самы обыкновенные, типа
var entityOne = this.DbContext.EntiesOne.SingleAsync(e1 => e1 == 123);
var entitiesTwo = this.DbContext.EntiesTwo.Where(e2 => e2.Abcd == entityOne.Abcd).ToListAsync();
что-то такое, не сложнее.
И еще раз, я подчеркну — до перехода с .NET 4.8 на .NET 5.0 всё работало (и продолжает работать) в высоконагруженной, продуктивной среде.
Re[2]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Здравствуйте, takTak, Вы писали:
T>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,
Scoped — Request Singleton. Большая разница с просто Singleton-ом
T>lazy loading используется? мне кажется, что подобные проблемы именно с этим связаны...
Хм ... должен был быть отключен, но я проверю завтра ещё раз
T>вообще, связка странная: адекватнее было бы имхо: .net core 3.1 + ef core 3.1
Вы вникали в портирование больших EF6 проектов на EF Core? Тут многое придется переписывать — еще то удовольствие ....
Да и зачем, если в .NET 5.0 (а я о нём писал) будет EF6?
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
T>>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно, YA>Scoped — Request Singleton. Большая разница с просто Singleton-ом
контейнер сменился: подразумеваю, что конвенции могут несколько отличаться
T>>lazy loading используется? мне кажется, что подобные проблемы именно с этим связаны... YA>Хм ... должен был быть отключен, но я проверю завтра ещё раз
ну раз раньше с async -await работало, то должно было быть отключено, иначе бы не работало: lazy loading происходит синхронно
T>>вообще, связка странная: адекватнее было бы имхо: .net core 3.1 + ef core 3.1 YA>Вы вникали в портирование больших EF6 проектов на EF Core? Тут многое придется переписывать — еще то удовольствие .... YA>Да и зачем, если в .NET 5.0 (а я о нём писал) будет EF6?
мне кажется, что ef core будет дальше развиваться, а вот ef- вряд ли, тем более, что в ef core они попытались сгенерированный sql оптимизировать, он теперь гораздо легче читается...
да, как раз сейчас портированием занят: пока в процессе
короче, я внимательнее присмотрелся бы к отличиям в di контейнерах, у майкрософта — местами довольно своеобразные представления: ты можешь зарегистрировать что-то как transient, туда за-inject-ить что-то как scoped и твой первый экземпляр станет scoped и т.д.
Re[4]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
у нас (после портирования) всё Scoped. В инжекторе NancyFX тоже не в лоб "Request Singleton" называлось, но смысл этот.
P.S. Мне моя попа тоже подсказывает, что что-то с Dependency Injection, но там всё не раз проверили, и оттестировали (см. мой первый постинг, в части про "эксперемента ради")
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
У вас именно так написано или все таки стоит await:
var entityOne = await this.DbContext.EntiesOne.SingleAsync(e1 => e1 == 123);
var entitiesTwo = await this.DbContext.EntiesTwo.Where(e2 => e2.Abcd == entityOne.Abcd).ToListAsync();
Из справки:
Remarks
Multiple active operations on the same context instance are not supported. Use 'await' to ensure
that any asynchronous operations have completed before calling another method on this context.
Re[4]: .NET 5.0, EF6, DependencyInjection и асинхронный WebA
Здравствуйте, B7_Ruslan, Вы писали:
B_R>У вас именно так написано или все таки стоит await:
await конечно, очепятался.
B_R>Multiple active operations on the same context instance are not supported. Use 'await' to ensure
Само собой.
-----
Я повторюсь — на .NET 4.8 всё работает, давно и активно, проблемы начались при переносе на .NET 5.0 (см. самое первое сообщение)
T>>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,
НС>Как scoped регистрировать контекст тоже не нужно. Его надо создавать явно.
ты предлагаешь вместо services.AddDbContext, который регистрирует по умолчанию scoped, самому ручками создавать DbContext с using ручками?
нафига?
кстати, рекомендую автору ветки поиграться с разными вариантами: перевести всё на transient, если используется AddDbContextPool, то зарегистриривать просто через AddDbContext, или наоборот, при AddDbContextPool вроде создаётся самый настоящий singleton
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
YA>Мы даже не знаем на что грешить — на Dependency Injector, на .NET Core (.NET 5.0), на хостинг в IIS, или еще на что! YA>Кто-то сталкивался с такой проблемой?
Здравствуйте, takTak, Вы писали:
T>ты предлагаешь вместо services.AddDbContext, который регистрирует по умолчанию scoped, самому ручками создавать DbContext с using ручками?
Да.
T>нафига?
Потому что такой подход обеспечивает хорошую видимость скоупа коннекта и транзакций и не позволяет писать кривой код, приводящий к проблемам типа описанной в стартовом сообщении.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Здравствуйте, varenikAA, Вы писали:
AA>Здравствуйте, Yuri Abele, Вы писали:
YA>>.EF6 => EF6 YA>>NancyFX => WebAPI
AA>Лишь замечу, что технологии все сложней, это надо понимать. AA>Но изучив, внезапно понимаешь, что это просто. AA>дело в опыте.
А к чему вы эту очевидную истину?
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
T>>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно, НС>Как scoped регистрировать контекст тоже не нужно. Его надо создавать явно.
Когда цепочка вызовов начинается в ответ на HTTP Request, а в DI всё scoped, то мы, удивительным образом, получаем Request Singleton, согласны?
На вопрос "а нафига Request Singleton" отвечают эти ограничения:
1. EF6 отказывается разгребать исменения в кэше, если они созданы параллельными (для общего DbContext) запросами.
Кстати, в MSSQL тоже невозможно создать External SP, которая многозадачна.
2. Т.к. п.1. и дабы избежать Dead-Lock-ов, EF6 заприщает более одной транзакции на один DbContext,
3. п.2. но при этом требует, чтобы все изменения одной транзакции были в одном DbContext-е. Здесь есть способы это обойти, но с лишними, никому не нужными, напрягами
Ну и еще одно — используя подход Request Singleton, скомбинировав его с одним Security Context-ом про один Request, мы получаем возможность реализовать Row-Level Security на уровне EF-кэша
Re[5]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>... кривой код, приводящий к проблемам типа описанной в стартовом сообщении.
Интересно ...
Поясните что у нас кривого?
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Нашлась причина ...
Коллега, который начинал всё это портирование, в самом начале, в контроллере WebAPI, в вызванном route-ингом асинхронном методе, при вызове асинхронного-же метода сервиса (DAL), умудрился забыть поставить await.
Это вот та самая старуха с её прорухой ...
Ну, бывает, за то скучать не пришлось
Всем спасибо за желание помочь!
Re[6]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
Здравствуйте, Yuri Abele, Вы писали:
НС>>... кривой код, приводящий к проблемам типа описанной в стартовом сообщении. YA>Интересно ... YA>Поясните что у нас кривого?
У вас где то явно используется контекст после того как его задиспозили. Если бы у вас контекст создавался в области видимости такого с гарантией не произошло бы.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI