Dependency Injection of DbContext in .Net Core console app: A second operation started on this context before a previous operation completed

.net-core c# dependency-injection entity-framework-core

Question

I have implemented a project on .NET Core 2.2 which consists of 1 console app and 1 class library. Console app is basically a consumer which subscribes to some topics and processes messages that come in. Class library is a database layer, where I have repository. The problem is that sometimes I get error "System.InvalidOperationException: A seconds operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe" when there is interaction with database.

I searched for similar problems, but couldn't find solution that would fix my issue. I have tried registering DbContext and IRepository both as Scoped and Transient, but still keep getting the error. Is it wrong the way I am trying to register DbContext?

This is my code in Consumer.cs

class Consumer
{
    static readonly IConfigurationRoot _configuration;
    static readonly IServiceProvider _serviceProvider;
    static readonly IRepository _repository;

    static Consumer()
    {
        _configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build();

        _serviceProvider = new ServiceCollection()
            .AddSingleton(_configuration)
            .AddTransient<IRepository, Repository>()
            .AddDbContext<QueueContext>(options => options.UseNpgsql(_configuration.GetConnectionString("Queue")), ServiceLifetime.Transient)
            .BuildServiceProvider();

        _repository = _serviceProvider.GetService<IRepository>();
    }

    static void Main(string[] args)
    {
        ...

        RunPoll(here some parameters);
    }

    private static void RunPoll(here some parameters)
    {
        ...

        consumer.OnMessage += async (_, msg) => await ProcessMessageAsync(consumer, msg);

        ...
    }

    private static async Task ProcessMessageAsync(here some parameters)
    {
        ...

        var message = _repository.GetQueueMessage(msg.Value.Id); // On this line I get exception

        if (message == null)
        {
            message = await _repository.AddQueueMessageAsync(msg.Value.Id); // On this line I get exception
        }

        while(message.NumTries < msg.Value.MaxNumAttempts)
        {
            message = await _repository.UpdateQueueMessageTriesAsync(message); // On this line I get exception too
        }

        ...
    }
}

This is my code in Respository.cs

public class Repository : IRepository
{
    private readonly QueueContext _db;

    public Repository(QueueContext db)
    {
        _db = db;
    }

    public QueueMessage GetQueueMessage(long id)
    {
        var message = (from qm in _db.QueueMessages
                       where qm.Id == id
                       select qm).FirstOrDefault();

        return message;
    }

    public async Task<QueueMessage> AddQueueMessageAsync(long id)
    {
        var message = new QueueMessage
        {
            Id = id,
            StartDate = DateTime.Now,
            LastTryDate = DateTime.Now,
            NumTries = 0
        }

        _db.QueueMessages.Add(message);
        await _db.SaveChangesAsync();

        return message;
    }

    public async Task<QueueMessage> UpdateQueueMessageTriesAsync(QueueMessage message)
    {
        if (message != null)
        {
            message.NumTries += 1;
            message.LastTryDate = DateTime.Now;

            _db.QueueMessages.Update(message);
            await _db.SaveChangesAsync();
        }

        return message;
    }
}

This is my code in IRepository.cs

public interface IRepository
{   
    QueueMessage GetQueueMessage(long id);
    Task<QueueMessage> AddQueueMessageAsync(long id);
    Task<QueueMessage> UpdateQueueMessageTriesAsync(QueueMessage message);
}
1
0
5/3/2019 6:03:59 PM

Popular Answer

This:

_serviceProvider.GetService

is not Dependency Injection. DI is when you define a constructor (or other method) taking an IRepository. Then you can have a Consumer instance with non-static methods, and the repository is appropriately created when the Consumer is created (or the method is called).

0
5/3/2019 9:34:37 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