I have a WebApi controller that has services injected by AutoFac in the OWIN Startup class
builder.Register(c => new MyEntities()).InstancePerRequest();
I have also tried
builder.Register(c => new MyEntities()).InstancePerLifetimeScope();
In a controller action I call a service method to create a new record, pass the id created to an external api through HttpClient to get some more data, then update the new record with some return data.
[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();
}
service code
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();
}
}
But I get an InvalidOperationException
in UpdateNewThing var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id);
System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.
It seems like I have to give up either injecting the context, async/await or use something like a contextfactory; unless anyone can spot something simple I have missed that would let me continue with this design.
Your code looks fine in a single-threaded context. However, DbContext
is not thread safe, and I suspect what is happening is you're executing CreateNewThing()
on one thread, and the task scheduler is in this case executing UpdateNewThing()
on a different thread.
Either way, a better metaphor is to use a context factory, which you inject into your IMyService
in this case, and then for every IMyService
method you create a new MyEntities
context in a using()
block.
DbContext
's are cheap to create and this is how they are intended to be used; long-lived contexts are almost always incorrect usage.
Edit 1 - example context factory as requested. I tend to implement a generic factory that can create multiple contexts, but that's probably moving outside the scope of this question.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With