如何修复 DbContext 已为异步场景处理?
How to fix DbContext has been disposed for an async scenario?
我们已将我们的应用程序从 NserviceBus v5 升级到 v6,之后我们 运行 遇到了主要问题,大部分时间收到以下错误。
The operation cannot be completed because the DbContext has been
disposed.
直到我们加载到我们的系统才明显。
我们有 运行 八个并发线程,因此我们收到上述错误。
public class EndpointInitializer
{
public void Initialize(IKernel container)
{
var endpointConfiguration = new EndpointConfiguration("MyEndpoint");
endpointConfiguration.UseContainer<NinjectBuilder>(
customizations => { customizations.ExistingKernel(container); });
//More settings...
}
}
.
public class MyMessageHandler : IHandleMessages<MyCommand>
{
private readonly IPersonRepository _personRepository;
public MyMessageHandler(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
public async Task Handle(MyCommand message, IMessageHandlerContext context)
{
var person = await _personRepository.GetByIdentifierAsync(message.Identifier).ConfigureAwait(false);
//More code...
await _personRepository.UpdateAsync(person);
}
}
。
[Serializable]
public class MyCommand
{
public string Identifier { get; set; }
}
.
public class DependencyRegistrar
{
public IKernel Container { get; set; }
public void Create()
{
Container = new StandardKernel();
RegisterTypes(Container);
}
public void RegisterTypes(IKernel kernel)
{
kernel.Bind<IPersonRepository>().To<PersonRepository>();
kernel.Bind<DbContext>().To<MyDbContext>().InThreadScope();
//More registrations...
}
}
。
public class MyDbContext : DbContext
{
public MyDbContext() : base("MyConn")
{
}
}
。
public interface IPersonRepository
{
Task<Person> GetByIdentifierAsync(string identifier);
Task UpdateAsync(Person entity);
//More methods...
}
。
public class PersonRepository : IPersonRepository
{
private readonly DbContext _dbContext;
public PersonRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Person> GetByIdentifierAsync(string identifier)
{
var personList = await _dbContext.Set<Person>().Where(x => x.Identifier == identifier).ToListAsync().ConfigureAwait(false);
//More code...
return personList.SingleOrDefault();
}
public async Task UpdateAsync(Person entity)
{
//More code...
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
。
public class Person
{
public int Id { get; set; }
public string Identifier { get; set; }
//More properties...
}
我们注意到一个可行的选项是使用 Particulars 示例获取 DataContext 以使用 UnitOfWorkSetupBehavior。但它不太适合我们的场景,因为我们有一个复杂的设置,服务和存储库在整个应用程序的构造函数中注入 DbContext。
也就是说,现在的(部分)解决方案是调用存储库上的方法,例如;
var person = await _personRepository.GetByIdentifierAsync(context.DataContext(), message.Identifier).ConfigureAwait(false);
但是现在,当我们 运行 考虑更复杂的场景时,这就不够了。
那么,我们缺少什么?这里真正的问题是什么?
Ninject PerThreadScope
使用 System.Threading.Thread.CurrentThread
。有了异步,线程可能会在每次延续时发生变化(await
语句后面的代码)。您可以使用 a custom named scope, an async local scope or use the InUnitOfWorkScope
from NServiceBus.Ninject.
我们已将我们的应用程序从 NserviceBus v5 升级到 v6,之后我们 运行 遇到了主要问题,大部分时间收到以下错误。
The operation cannot be completed because the DbContext has been disposed.
直到我们加载到我们的系统才明显。
我们有 运行 八个并发线程,因此我们收到上述错误。
public class EndpointInitializer
{
public void Initialize(IKernel container)
{
var endpointConfiguration = new EndpointConfiguration("MyEndpoint");
endpointConfiguration.UseContainer<NinjectBuilder>(
customizations => { customizations.ExistingKernel(container); });
//More settings...
}
}
.
public class MyMessageHandler : IHandleMessages<MyCommand>
{
private readonly IPersonRepository _personRepository;
public MyMessageHandler(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
public async Task Handle(MyCommand message, IMessageHandlerContext context)
{
var person = await _personRepository.GetByIdentifierAsync(message.Identifier).ConfigureAwait(false);
//More code...
await _personRepository.UpdateAsync(person);
}
}
。
[Serializable]
public class MyCommand
{
public string Identifier { get; set; }
}
.
public class DependencyRegistrar
{
public IKernel Container { get; set; }
public void Create()
{
Container = new StandardKernel();
RegisterTypes(Container);
}
public void RegisterTypes(IKernel kernel)
{
kernel.Bind<IPersonRepository>().To<PersonRepository>();
kernel.Bind<DbContext>().To<MyDbContext>().InThreadScope();
//More registrations...
}
}
。
public class MyDbContext : DbContext
{
public MyDbContext() : base("MyConn")
{
}
}
。
public interface IPersonRepository
{
Task<Person> GetByIdentifierAsync(string identifier);
Task UpdateAsync(Person entity);
//More methods...
}
。
public class PersonRepository : IPersonRepository
{
private readonly DbContext _dbContext;
public PersonRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Person> GetByIdentifierAsync(string identifier)
{
var personList = await _dbContext.Set<Person>().Where(x => x.Identifier == identifier).ToListAsync().ConfigureAwait(false);
//More code...
return personList.SingleOrDefault();
}
public async Task UpdateAsync(Person entity)
{
//More code...
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
。
public class Person
{
public int Id { get; set; }
public string Identifier { get; set; }
//More properties...
}
我们注意到一个可行的选项是使用 Particulars 示例获取 DataContext 以使用 UnitOfWorkSetupBehavior。但它不太适合我们的场景,因为我们有一个复杂的设置,服务和存储库在整个应用程序的构造函数中注入 DbContext。
也就是说,现在的(部分)解决方案是调用存储库上的方法,例如;
var person = await _personRepository.GetByIdentifierAsync(context.DataContext(), message.Identifier).ConfigureAwait(false);
但是现在,当我们 运行 考虑更复杂的场景时,这就不够了。
那么,我们缺少什么?这里真正的问题是什么?
Ninject PerThreadScope
使用 System.Threading.Thread.CurrentThread
。有了异步,线程可能会在每次延续时发生变化(await
语句后面的代码)。您可以使用 a custom named scope, an async local scope or use the InUnitOfWorkScope
from NServiceBus.Ninject.