ASP.NET Core Web API, inject database context into exception handling

ASP.NET Core Web API, inject database context into exception handling

Problem Description:

I’m trying to write custom exception handling that would write error details into the database.

Right now (in the code I inherited) the DB context is injected into static class with extension method:

Startup.cs:

ExceptionMiddleware.ExceptionMiddlewareConstructor(app.ApplicationServices.GetService<IErrorLoggerService>());

ExceptionMiddleware.cs:

public static class ExceptionMiddleware
{
    private static IErrorLoggerService _service;
    public static void ExceptionMiddlewareConstructor(IErrorLoggerService service)
    {
        _service = service;
    }
    public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILog logger)
    {
        app.UseExceptionHandler(appError =>
        {
            appError.Run(async context =>
            {
                if (context != null)
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var requestString = "Query: " + context.Request.QueryString.Value;

                    if (context.Request.Method != "GET")
                    {
                        context.Request.Body.Position = 0;
                        using StreamReader reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true);
                        requestString += "Body" + await reader.ReadToEndAsync();
                    }

                    var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                    if (contextFeature != null)
                    {
                        var claims = context.User.Claims.ToList();

                        //Filter specific claim    
                        var email = claims.FirstOrDefault(x =>
                            x.Type.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
                                StringComparison.OrdinalIgnoreCase))?.Value;

                        if (contextFeature.Error.StackTrace != null)
                        {
                            var error = new ERROR
                            {
                                //assinging property values for DB entity
                            };

                            await _service.CreateAsync(error);
                        }
                    }
                }
            });
        });
    }
}

ErrorLoggerService is a service that handles db write for ERROR entities, and has DbContext injected into constructor.

Obviously current code is not thread-safe because of the captured dependency to DbContext, but I’m not sure how to inject DbContext into exception handler properly

Solution – 1

If you registed your dbcontext as a scoped service by default,

// If you didn't pass the parameter of servicelife time into the method,the lifetime would be scoped by default
builder.Services.AddDbContext<ZipTestContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("SomeDbContext") ?? throw new InvalidOperationException("Connection string 'SomeDbContext' not found.")),ServiceLifetime.Scoped);

you could get the dbcontext from container as below:

var dbcontext = context.RequestServices.CreateScope().ServiceProvider.GetService<SomeDbContext>();

And the case related may help:Why cannot resolve DbContext in constructor of custom middleware?

Tried as below:

exceptionHandlerApp.Run(async context =>
    {
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        // using static System.Net.Mime.MediaTypeNames;
        context.Response.ContentType = Text.Plain;


        //var dbcontext = context.RequestServices.GetService<ZipTestContext>();
        var dbcontext = context.RequestServices.CreateScope().ServiceProvider.GetService<ZipTestContext>();
        var somemodel = new SomeModel() { Description = "Description", Name = "SomeName" };
        dbcontext?.Add(somemodel);
        dbcontext?.SaveChangesAsync();

        await context.Response.WriteAsync("An exception was thrown.");

        var exceptionHandlerPathFeature =
            context.Features.Get<IExceptionHandlerPathFeature>();

        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            await context.Response.WriteAsync(" The file was not found.");
        }

        if (exceptionHandlerPathFeature?.Path == "/")
        {
            await context.Response.WriteAsync(" Page: Home.");
        }
    });
});

You could see the different performance between

var dbcontext = context.RequestServices.GetService<ZipTestContext>();
            var dbcontext = 

And

context.RequestServices.CreateScope().ServiceProvider.GetService<ZipTestContext>();

Result1:

enter image description here

Result2:

enter image description here

Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject