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

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

Question

We have upgraded our application from NserviceBus v5 to v6 and after that we run into major problem, most of the time receiving the following error.

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

It was not obvious until we got load to our system.

We are running with eight concurrent threads and by that we receive the above error.

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

One option that we noticed is working is to pick up DataContext using Particulars example to use UnitOfWorkSetupBehavior. But it does not fit that well in our scenario because we have a complicated setup with services and repositories injecting the DbContext in the constructor throughout our application.

Ie, the (partial) solution for now is to call the method on the repository like;

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

But now, when we run inte more complicated scenarios this won¨t suffice.

So, what are we missing? What is really the issue here?

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

Accepted Answer

Ninject PerThreadScope uses System.Threading.Thread.CurrentThread. With async in place the thread can change potentially for every continuation (the code that follows an await statement). You can either use a custom named scope, an async local scope or use the InUnitOfWorkScope from NServiceBus.Ninject.

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