I am implementing an API and as part of it I have setup a custom .Net Middleware service extension UseRequestLoggingModdlewareExtension()
that it run between the following:
app.UseHttpsRedirection();
app.UseRequestLoggingModdlewareExtension();
app.UseRouting();
The code is simple, and just logs the output of the request into a custom table.
public async Task InvokeAsync(HttpContext httpContext)
{
var stopAccess = _keyManager.getKeyValue("stopImmediateAccess");
if (!Convert.ToBoolean(stopAccess))
{
await _next(httpContext);
var loggingLevel = _keyManager.getKeyValue("loggingLevel");
if (loggingLevel != null)
{
if (loggingLevel.ToLower() == "information")
{
var userIdClaim = httpContext.User.FindFirst("userid")?.Value;
int? userId = null;
if(userIdClaim != null)
{
userId = Int32.Parse(userIdClaim);
}
var logging = new ApiRequestLogging
{
userId = userId,
remoteIp = httpContext.Connection.RemoteIpAddress.ToString() == "::1" ? "localhost" : httpContext.Connection.RemoteIpAddress.ToString(),
userAgent = httpContext.Request.Headers["User-Agent"].ToString(),
requestMethod = httpContext.Request.Method,
requestUrl = httpContext.Request.Path,
queryString = httpContext.Request.QueryString.ToString(),
requestHeaders = String.Join(",", httpContext.Request.Headers),
responseCode = httpContext.Response.StatusCode,
responseHeaders = String.Join(",", httpContext.Response.Headers),
createdDt = DateTime.Now
};
_logging.LogApiRequest(logging);
}
}
}
}
Where I am struggling is with some errors regarding some issues with the DBContext.
System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
The error is appearing twice, at both lines where the _keyManager service is called. The keyManager service is simply doing the following:
public string getKeyValue(string keyName)
{
var value = _context.keyManagement.Where(k => k.keyName == keyName).Select(v => v.keyValue).FirstOrDefault();
return value;
}
I have a suspicion that it could be something to do with the ‘await’ and the aschronousness of the code, however I have tried multiple combinations and cannot seem to bypass this issue.
2
Answers
Do you implement the IMiddleware interface i.e. something like this
RequestLoggingMiddleware: IMiddleware
This will resolve your middleware through the IMiddlewareFactory like a scoped service and inject the other dependant services. Your middleware ctor should be something likeRequestLoggingMiddleware(IKeyManagerService keyManager)
This way the middleware will be activated per client request i.e. scoped and not in the normal way as a singleton. Providing scoped middleware instances per request will allow you to use short-lived ApplicationDbContext in the middleware or its dependent services:or in your case more like
This way the
ApplicationDbContext
used bykeyManager
service will be created per request and disposed of after the request is completed. Of course, theIKeyManagerService
should be registered as a scoped service as well.Thats why i like to use
IDisposable
interface with DbContext.