Search code examples
asp.net-core-mvcdbcontextmiddlewareentity-framework-coreasp.net-core-1.0

Entity Framework Core 1.0 DbContext not scoped to http request


I understood by watching this video with Rowan Miller https://channel9.msdn.com/Series/Whats-New-with-ASPNET-5/06 (at minute 22) that the way of configuring Entity Framework Core (previously known as EF7) into an ASP.NET Core 1.0 app (previously known as ASP.NET 5) in Startup.cs is as follows:

    public void ConfigureServices(IServiceCollection services)
    {
        //Entity Framework 7 scoped per request??
        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<MyDbContext>(options =>
            {
                options
                  .UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
            });

        //MVC 6
        services.AddMvc();
    }

and that this DbContext will be scoped to an http request so that whenever in the code throughout the http pipeline (including middleware or MVC) a DbContext is used, we know for sure that the instance injected by the DI container will be the same.

But the problem is that it does not seem to work that way. Within the lifetime of MVC it is true that the DbContext instance injected is the same, but as described here: Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter I am trying to plug into the pipeline the following middleware to achieve some kind of centralised Commit/Rollback after a controller finalises execution:

public class UnitOfWorkMiddleware
{
    private readonly RequestDelegate _next;
    private readonly MyDbContext _dbContext;
    private readonly ILogger _logger;

    public UnitOfWorkMiddleware(RequestDelegate next, MyDbContext dbContext, ILoggerFactory loggerFactory)
    {
        _next = next;
        _dbContext = dbContext;
        _logger = loggerFactory.CreateLogger<UnitOfWorkMiddleware>();
    }

    public async Task Invoke(HttpContext httpContext)
    {
        await _next.Invoke(httpContext);
        _logger.LogInformation("Saving changes for unit of work if everything went good");
        await _dbContext.SaveChangesAsync();
    }
}

and this middleware is immediately before than MVC6 in the pipeline

//inside Configure(IApplicationBuilder app) in Startup.cs
app.UseMiddleware<UnitOfWorkMiddleware>();
app.UseMvcWithDefaultRoute();

The DbContext instance in my Middleware is not the same as the instance being injected during the MVC lifetime.

Is this expected? Shouldn't a DbContext be scoped to an http request? Is possible to achieve what I was trying to achieve?

The plan B would be to use an MVC 6 Global filter (if I can find some documentation on how to do this). I assume that being part of MVC 6 framework, the DbContext instance injected will be the same..


Solution

  • After more testing I can confirm that the DbContext is scoped to the http request only during the lifetime of the MVC execution (maybe MVC is in charge of disposing of the DbContext), so any middleware before or after in the pipeline won't have the same instance of DbContext injected.

    I decided then to add a global filter to MVC 6 (because filters are part of MVC framework) so that I can access the same DbContext instance before and after the action's execution.

    If anybody is interested on how to create this global filter check: Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter