【问题标题】:.NET API threading issue (DBContext).NET API 线程问题 (DBContext)
【发布时间】:2022-02-16 22:42:17
【问题描述】:

我正在实现一个 API,作为其中的一部分,我设置了一个自定义 .Net 中间件服务扩展 UseRequestLoggingModdlewareExtension(),它在以下之间运行:

app.UseHttpsRedirection();

app.UseRequestLoggingModdlewareExtension();

app.UseRouting();

代码很简单,只是将请求的输出记录到自定义表格中。

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);
                    }
                }
            }
        }

我正在苦苦挣扎的地方是关于 DBContext 的一些问题的一些错误。

System.InvalidOperationException:在前一个操作完成之前,在此上下文上启动了第二个操作。这通常是由不同的线程同时使用同一个 DbContext 实例引起的。有关如何避免 DbContext 线程问题的更多信息,请参阅https://go.microsoft.com/fwlink/?linkid=2097913。

错误出现了两次,出现在调用 _keyManager 服务的两行。 keyManager 服务只是执行以下操作:

public string getKeyValue(string keyName)
        {
            var value = _context.keyManagement.Where(k => k.keyName == keyName).Select(v => v.keyValue).FirstOrDefault();
            return value;
        }

我怀疑这可能与“等待”和代码的异步性有关,但是我尝试了多种组合,似乎无法绕过这个问题。

【问题讨论】:

  • 一般来说,初学者的一个常见错误是没有意识到数据库上下文是轻量级对象,应该为每个查询或一组相关查询(可能是会话)创建和销毁数据库.它们不应该长时间徘徊或跨线程使用。考虑这是否是您的问题的原因。 (点击错误消息中的链接。)
  • 你能展示你的服务注入配置吗?这可能是由于 keymanager 的作用域造成的,但中间件是单例的。见docs.microsoft.com/en-us/aspnet/core/fundamentals/…
  • 异步调用问题。看到这个-stackoverflow.com/questions/44237977/…。同时,尝试对数据库上下文使用using 语句,因为这确认每个打开的数据库连接都已正确关闭。

标签: c# asp.net entity-framework asp.net-web-api dbcontext


【解决方案1】:

您是否实现了 IMiddleware 接口,即类似 RequestLoggingMiddleware: IMiddleware 这样的东西,这将通过 IMiddlewareFactory 解析您的中间件,就像一个作用域服务并注入其他依赖服务。您的中间件 ctor 应该类似于 RequestLoggingMiddleware(IKeyManagerService keyManager) 这样中间件将根据客户端请求被激活,即作用域而不是以正常方式作为单例。为每个请求提供作用域中间件实例将允许您在中间件或其依赖服务中使用短暂的 ApplicationDbContext:

public RequestLoggingMiddleware(ApplicationDbContext  db)
{
    _db = db;
}

或者在你的情况下更像

public class RequestLoggingMiddleware: IMiddleware 
{
   public RequestLoggingMiddleware(IKeyManagerService keyManager)
   {
       _keyManager = keyManager;
   }
}

public KeyManagerService(ApplicationDbContext  db) 
{
    _db = db;
} 

services.AddScoped<IKeyManagerService, KeyManagerService>()

这样keyManager 服务使用的ApplicationDbContext 将根据请求创建并在请求完成后处理掉。当然,IKeyManagerService 也应该注册为范围服务。

【讨论】:

    【解决方案2】:

    这就是为什么我喜欢在 DbContext 中使用IDisposable 接口。

    public string getKeyValue(string keyName)
    {
        string value = null;
        using(var _cnx = new DbContext())
        {
                value = _cnx.keyManagement.Where(k => k.keyName == keyName).Select(v => v.keyValue).FirstOrDefault();
                
        }       
        return value;
    }
    
    

    【讨论】:

      猜你喜欢
      • 2011-07-28
      • 2012-09-20
      • 1970-01-01
      • 2011-02-27
      • 2023-03-21
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      相关资源
      最近更新 更多