EF + AutoFac + async"连接的当前状态正在连接"

Pau*_*wat 5 c# autofac async-await entity-framework-6 asp.net-web-api2

我有一个WebApi控制器,它在OWIN Startup类中有AutoFac注入的服务

builder.Register(c => new MyEntities()).InstancePerRequest();
Run Code Online (Sandbox Code Playgroud)

我也试过了

builder.Register(c => new MyEntities()).InstancePerLifetimeScope();
Run Code Online (Sandbox Code Playgroud)

在控制器操作中,我调用服务方法来创建新记录,通过HttpClient将创建的id传递给外部api以获取更多数据,然后使用一些返回数据更新新记录.

[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();
}
Run Code Online (Sandbox Code Playgroud)

服务代码

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我InvalidOperationException在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.
Run Code Online (Sandbox Code Playgroud)

好像我不得不放弃注入上下文,异步/等待或使用像contextfactory这样的东西; 除非有人能发现我错过的简单内容,否则我会继续这个设计.

sel*_*ape 2

您的代码在单线程上下文中看起来很好。但是,DbContext它不是线程安全的,我怀疑发生的情况是您CreateNewThing()在一个线程上执行,而任务调度程序在这种情况下UpdateNewThing()在不同的线程上执行。

无论哪种方式,更好的比喻是使用上下文工厂,IMyService在本例中将其注入到您的上下文工厂中,然后为每个方法在块中IMyService创建一个新的上下文。MyEntitiesusing()

DbContext的创建成本低廉,这就是它们的使用方式;长期存在的上下文几乎总是不正确的用法。

根据要求编辑 1 - 示例上下文工厂。我倾向于实现一个可以创建多个上下文的通用工厂,但这可能超出了这个问题的范围。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)