How to fix DbContext has been disposed for an async scenario?

c# entity-framework-6 ninject nservicebus repository-pattern

Question

After moving from NserviceBus version 5 to version 6, we had significant issues and often saw the following error.

The operation cannot be completed because the DbContext has been disposed.

Before we loaded our system, it was not immediately apparent.

We see the aforementioned issue because eight threads are executing concurrently.

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...
}

Picking up DataContext from the Particulars example and using it with the code UnitOfWorkSetupBehavior is one way that we have seen to work. We have a complex structure with services and repositories that inject the DbContext in the function Object() { [native code] } throughout our programme, hence it does not match our case very well.

For instance, calling the method on the repository in the manner;

    var person = await _personRepository.GetByIdentifierAsync(context.DataContext(), message.Identifier).ConfigureAwait(false);

However, this won't work when we go through more complex situations.

What are we lacking, then? What exactly is the problem here?

1
0
6/16/2018 8:57:52 PM

Accepted Answer

Ninject PerThreadScope uses System.Threading.Thread.CurrentThread . The thread might possibly change when async is enabled for every continuation (the code that comes after anawait statement). Choose from the a certain designated scope, local async scope, or theInUnitOfWorkScope from 25 to Zzz.

3
6/17/2018 8:06:13 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow