Entity Framework Core: different threads using the same instance of DbContext

asp.net-core entity-framework entity-framework-core multithreading

Question

The application was developed on ASP NET Core 3. To log user actions, I decided to use a single method in the Project class. Faced the problem of using one singleton dbContext from different threads.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    string connection = Configuration.GetConnectionString("ConnectionDB");
    services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);

    services.AddSingleton<Project>();
}

Project.cs

public async Task AddUserLog(string action, string message, int userId)
{
    try
    {
        UserLog userLog = new UserLog()
        {
            Action = action,
            Message = message,
            UserId = userId
            Datepoint = DateTime.Now
        };

        _dbContext.UserLog.Add(userLog);
        await _dbContext.SaveChangesAsync();
    }
    catch (Exception ex)
    {
        await AddSystemLog("Project", "AddUserLog", ex.Message);
    }
}

SchemeController.cs

public class SchemeController : ControllerBase
{
    private readonly Project _project;

    public SchemeController(Project project)
    {
        _project = project;
    }

    [Authorize(Policy = "AdvancedControl")]
    [HttpPost("[action]")]
    public async Task SomeMethode()
    {
        for (int i = 0; i < 10; i++)
        {
            await _project.AddUserLog("Text", "Message", 42);
        }       
    }
}

Already at the second iteration of the loop, I catch an exception in the AddUserLog method: "A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext."

I suggest several solutions:

  1. Add the log to the buffer table and then save it to the database by timer. But this is not the best way out;

  2. Block the method while it is being saved to the database.

But I don’t like any of the options.

Please tell me the correct approach in solving this issue.

1
0
11/11/2019 1:14:46 PM

Accepted Answer

So, you trying to use shared resource (singleton Project class) to perform parallel operations (save UserLogs) while your shared resource implementation is not thread-safe (exceptions raised).

You have at lease three ways to solve this:

  1. Do not use shared resource: register Project per scope instead of singletone;
  2. Do not perform operations in parallel: seems hard to achieve because you making webapp and you can't force user(s) to wait
  3. Refactor your resource to be thread-safe: add locks/mutexes/buffering... inside Project

There is no one "correct" way - all 3 are correct. Choose one you like (or combine several).

Usually using scoped dbcontext is recommended (because connections are pooled), but it's the creator of app who should decide.

1
11/11/2019 1:31:34 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