¿Cuál es la mejor práctica en EF Core para usar llamadas asíncronas paralelas con un Inyectado de contexto inyectado?
Tengo una API .NET Core 1.1 con EF Core 1.1 y utilizo la configuración estándar de Microsoft de usar la Inyección de dependencias para proporcionar el DbContext a mis servicios. (Referencia:https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#register-the-context-with-dependency-injection)
Ahora, estoy buscando paralelizar las lecturas de bases de datos como una optimización usandoCuando todo
Entonces en lugar de:
var result1 = await _dbContext.TableModel1.FirstOrDefaultAsync(x => x.SomeId == AnId);
var result2 = await _dbContext.TableModel2.FirstOrDefaultAsync(x => x.SomeOtherProp == AProp);
Yo suelo:
var repositoryTask1 = _dbContext.TableModel1.FirstOrDefaultAsync(x => x.SomeId == AnId);
var repositoryTask2 = _dbContext.TableModel2.FirstOrDefaultAsync(x => x.SomeOtherProp == AProp);
(var result1, var result2) = await (repositoryTask1, repositoryTask2 ).WhenAll();
Esto está muy bien, hasta que use la misma estrategia fuera de estas clases de acceso al Repositorio de DB y llame a estos mismos métodos con WhenAll en mi controlador a través de múltiples servicios:
var serviceTask1 = _service1.GetSomethingsFromDb(Id);
var serviceTask2 = _service2.GetSomeMoreThingsFromDb(Id);
(var dataForController1, var dataForController2) = await (serviceTask1, serviceTask2).WhenAll();
Ahora, cuando llamo a esto desde mi controlador, aleatoriamente obtendré errores de concurrencia como:
System.InvalidOperationException: ExecuteReader requiere una conexión abierta y disponible. El estado actual de la conexión está cerrado.
Creo que la razón es porque a veces estos hilos intentan acceder a las mismas tablas al mismo tiempo.Sé que esto es por diseño en EF Core y si quisiera, podría crear un nuevo dbContext cada vez, pero estoy tratando de ver si hay una solución alternativa. Fue entonces cuando encontré esta buena publicación de Mehdi El Gueddari:http://mehdi.me/ambient-dbcontext-in-ef6/
En el que reconoce esta limitación:
un DbContext inyectado le impide poder introducir múltiples subprocesos o cualquier tipo de flujos de ejecución paralelos en sus servicios.
Y ofrece una solución personalizada conDbContextScope
.
Sin embargo, presenta una advertencia incluso con DbContextScope en el sentido de que no funcionará en paralelo (lo que estoy tratando de hacer anteriormente):
Si intenta iniciar múltiples tareas paralelas dentro del contexto de un DbContextScope (por ejemplo, al crear múltiples subprocesos o múltiples tareas TPL), se encontrará con grandes problemas. Esto se debe a que el DbContextScope ambiental fluirá a través de todos los subprocesos que están utilizando sus tareas paralelas.
Su punto final aquí me lleva a mi pregunta:
En general, la paralelización del acceso a la base de datos dentro de una sola transacción comercial tiene pocos o ningún beneficio y solo agrega una complejidad significativa. Cualquier operación paralela realizada dentro del contexto de una transacción comercial no debe acceder a la base de datos.
¿No debería estar usando WhenAll en este caso en mis Controladores y seguir con el uso de wait-by-one? ¿O es la inyección de dependencia del DbContext el problema más fundamental aquí, por lo tanto, algún tipo de fábrica debería crear / suministrar uno nuevo cada vez?