.NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Yuri Abele Германия yabele.blogspot.com
Дата: 16.09.20 20:06
Оценка:
Всем привет!

Имеется:

Написанный на .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, или еще на что!
Кто-то сталкивался с такой проблемой?
.net 5.0 ef6 webapi dependencyinjection async
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: takTak  
Дата: 16.09.20 21:30
Оценка: +1
не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,
lazy loading используется? мне кажется, что подобные проблемы именно с этим связаны...

вообще, связка странная: адекватнее было бы имхо: .net core 3.1 + ef core 3.1
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: B7_Ruslan  
Дата: 16.09.20 21:39
Оценка:
Образец кода желательно показать. Запросы только на чтение?
Re[2]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Yuri Abele Германия yabele.blogspot.com
Дата: 16.09.20 21:46
Оценка:
Здравствуйте, 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
От: Yuri Abele Германия yabele.blogspot.com
Дата: 16.09.20 21:52
Оценка:
Здравствуйте, 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
От: takTak  
Дата: 16.09.20 22:07
Оценка:
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
От: Yuri Abele Германия yabele.blogspot.com
Дата: 16.09.20 22:22
Оценка:
Здравствуйте, takTak, Вы писали:

у нас (после портирования) всё Scoped. В инжекторе NancyFX тоже не в лоб "Request Singleton" называлось, но смысл этот.

P.S. Мне моя попа тоже подсказывает, что что-то с Dependency Injection, но там всё не раз проверили, и оттестировали (см. мой первый постинг, в части про "эксперемента ради")
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: B7_Ruslan  
Дата: 17.09.20 05:10
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

YA>var entityOne = this.DbContext.EntiesOne.SingleAsync(e1 => e1 == 123);

YA>var entitiesTwo = this.DbContext.EntiesTwo.Where(e2 => e2.Abcd == entityOne.Abcd).ToListAsync();

У вас именно так написано или все таки стоит 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
От: Yuri Abele Германия yabele.blogspot.com
Дата: 17.09.20 06:33
Оценка:
Здравствуйте, 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 (см. самое первое сообщение)
Отредактировано 17.09.2020 6:35 Yuri Abele . Предыдущая версия .
Re[2]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Ночной Смотрящий Россия  
Дата: 17.09.20 07:46
Оценка: +1
Здравствуйте, takTak, Вы писали:

T>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,


Как scoped регистрировать контекст тоже не нужно. Его надо создавать явно.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: takTak  
Дата: 17.09.20 08:31
Оценка:
T>>не совсем понятно, зачем ef регистроровать как singleton, вроде как scoped будет тоже достаточно,

НС>Как scoped регистрировать контекст тоже не нужно. Его надо создавать явно.


ты предлагаешь вместо services.AddDbContext, который регистрирует по умолчанию scoped, самому ручками создавать DbContext с using ручками?
нафига?

кстати, рекомендую автору ветки поиграться с разными вариантами: перевести всё на transient, если используется AddDbContextPool, то зарегистриривать просто через AddDbContext, или наоборот, при AddDbContextPool вроде создаётся самый настоящий singleton
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: ksg71 Германия  
Дата: 17.09.20 09:15
Оценка: +1
Здравствуйте, Yuri Abele, Вы писали:



YA>Мы даже не знаем на что грешить — на Dependency Injector, на .NET Core (.NET 5.0), на хостинг в IIS, или еще на что!

YA>Кто-то сталкивался с такой проблемой?

поломаться могло из-за того что di контейнер следит за наличием dispose, прежний не делал видимо.
у вас он считает что пора диспозить, так например https://thinkrethink.net/2017/12/22/cannot-access-a-disposed-object-in-asp-net-core-when-injecting-dbcontext/
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: varenikAA  
Дата: 17.09.20 09:42
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

YA>.EF6 => EF6

YA>NancyFX => WebAPI

Лишь замечу, что технологии все сложней, это надо понимать.
Но изучив, внезапно понимаешь, что это просто.
дело в опыте.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[4]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Ночной Смотрящий Россия  
Дата: 17.09.20 10:14
Оценка:
Здравствуйте, 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
От: Yuri Abele Германия yabele.blogspot.com
Дата: 17.09.20 12:36
Оценка:
Здравствуйте, varenikAA, Вы писали:

AA>Здравствуйте, Yuri Abele, Вы писали:


YA>>.EF6 => EF6

YA>>NancyFX => WebAPI

AA>Лишь замечу, что технологии все сложней, это надо понимать.

AA>Но изучив, внезапно понимаешь, что это просто.
AA>дело в опыте.

А к чему вы эту очевидную истину?
Re[3]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Yuri Abele Германия yabele.blogspot.com
Дата: 17.09.20 12:49
Оценка:
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
От: Yuri Abele Германия yabele.blogspot.com
Дата: 17.09.20 12:50
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>... кривой код, приводящий к проблемам типа описанной в стартовом сообщении.

Интересно ...
Поясните что у нас кривого?
Re: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Yuri Abele Германия yabele.blogspot.com
Дата: 17.09.20 12:57
Оценка: 121 (3) :)
Нашлась причина ...
Коллега, который начинал всё это портирование, в самом начале, в контроллере WebAPI, в вызванном route-ингом асинхронном методе, при вызове асинхронного-же метода сервиса (DAL), умудрился забыть поставить await.
Это вот та самая старуха с её прорухой ...
Ну, бывает, за то скучать не пришлось

Всем спасибо за желание помочь!
Re[6]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Ночной Смотрящий Россия  
Дата: 17.09.20 13:59
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

НС>>... кривой код, приводящий к проблемам типа описанной в стартовом сообщении.

YA>Интересно ...
YA>Поясните что у нас кривого?

У вас где то явно используется контекст после того как его задиспозили. Если бы у вас контекст создавался в области видимости такого с гарантией не произошло бы.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[4]: .NET 5.0, EF6, DependencyInjection и асинхронный WebAPI
От: Ночной Смотрящий Россия  
Дата: 17.09.20 14:00
Оценка:
Здравствуйте, Yuri Abele, Вы писали:

YA>На вопрос "а нафига Request Singleton" отвечают эти ограничения:


Ты не понял. Я не предлагаю делать контекст синглтоном, я наоборот предлагаю его создавать по месту явно.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.