work unity unit pattern mvc injection generic framework example dependency c# async-await entity-framework-6 asp.net-web-api2 autofac

c# - pattern - unity web api



EF+AutoFac+async "El estado actual de la conexión se está conectando" (1)

Tengo un controlador WebApi que tiene servicios inyectados por AutoFac en la clase de inicio de OWIN

builder.Register(c => new MyEntities()).InstancePerRequest();

También he intentado

builder.Register(c => new MyEntities()).InstancePerLifetimeScope();

En una acción de controlador llamo a un método de servicio para crear un nuevo registro, paso el ID creado a una API externa a través de HttpClient para obtener más datos, luego actualizo el nuevo registro con algunos datos de retorno.

[HttpPost, Route("")] public async Task<IHttpActionResult> MyControllerAction(MyModel model) { var id = await _MyService.CreateNewThing(model.SomeId); var externalData = await CallExternalApiThroughHttpClient(id); await _MyService.UpdateNewThing(id, externalData); return Ok(); }

código de servicio

public class MyService : IMyService { private MyEntities _context; public MyService(MyEntities context) { _context = context; } public async Task<int> CreateNewThing(int someId) { var thing = new Thing { SomeId = someId }; _context.Things.Add(thing); await _context.SaveChangesAsync(); return thing.Id; } public async Task UpdateNewThing(int id, string externalDataField) { var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id); if (thing == null) { throw new ServiceNotFoundException("Thing " + transactionId + " not found"); } thing.ExternalDataField= externalDataField; await _context.SaveChangesAsync(); } }

Pero obtengo una InvalidOperationException en InvalidOperationException var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id);

System.InvalidOperationException: The connection was not closed. The connection''s current state is connecting.

Parece que tengo que renunciar a inyectar el contexto, asincr / aguardar o usar algo como una contextfactory; a menos que alguien pueda detectar algo simple que me he perdido que me permita continuar con este diseño.


Su código se ve bien en un contexto de subproceso único . Sin embargo, DbContext no es seguro para subprocesos, y sospecho que lo que está sucediendo es que está ejecutando CreateNewThing() en un subproceso, y el planificador de tareas está en este caso ejecutando UpdateNewThing() en un subproceso diferente.

De cualquier manera, una mejor metáfora es usar una fábrica de contexto, que usted inyecta en su IMyService en este caso, y luego para cada método de IMyService crea un nuevo contexto de MyEntities en un bloque using() .

DbContext son baratos de crear y así es como están destinados a ser utilizados; contextos de larga vida son casi siempre uso incorrecto.

Editar 1 - fábrica de contexto de ejemplo según lo solicitado. Tiendo a implementar una fábrica genérica que puede crear contextos múltiples, pero eso probablemente se está moviendo fuera del alcance de esta pregunta.

public interface IMyEntitiesFactory { MyEntities Create(); } public class MyEntitiesFactory : IMyEntitiesFactory { MyEntities IMyEntitiesFactory.Create() { return new MyEntities(); } } // For use with unit tests; e.g. pass a mock object to the constructor. public class TestMyEntitiesFactory : IMyEntitiesFactory { private readonly MyEntities _value; public TestMyEntitiesFactory(MyEntities value) { _value = value; } MyEntities IMyEntitiesFactory.Create() { return _value; } }