在DI环境中使用时,奇怪而难以调试错误



im当前使用autoFac在IOC设置中使用EF6。在我的服务中,我像这样注入我的dbcontext:

    private readonly CommerceContext _dbContext;
    public UserTokenService(CommerceContext dbContext)
    {
        _dbContext = dbContext;
    }

当我更新我的令牌时,随机发生了几个不同的SQL错误。

我的apicontroller:

private readonly IUserTokenService _tokenService;
public UsersApiController(IUserTokenService tokenService)
{
    this._tokenService = tokenService;
}
[RequireHttps]
[System.Web.Http.HttpGet]
[System.Web.Http.Route("api/users/validatetoken")]
public IHttpActionResult ValidateToken(Guid tokenId)
{
    var falseObj = new
    {
        IsValid = false,
        Email = string.Empty
    };
    var token = _tokenService.Get(tokenId);
    if (token == null) return Ok(falseObj);
    if (token.IsExpired) return Ok(falseObj);
    var user = _userService.Find(token.UserId);
    if (user == null) return Ok(falseObj);
    _tokenService.Update(token);
}

我的tokenservice获取方法:

    public UserToken Get(Guid token)
    {
        return _dbContext.UserTokens.FirstOrDefault(x => x.Token == token);
    }

我的tokenservice更新方法:

    public void Update(UserToken token)
    {
        token.ExpiresAt = DateTime.UtcNow.AddHours(1);
        _dbContext.SaveChanges();
    }

_dbContext包含我所有不同的DBSET,并且如下注册:

  var builder = new ContainerBuilder();
  builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);
  builder.RegisterControllers(Assembly.GetExecutingAssembly());
  builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
  builder.RegisterModule(new AutofacWebTypesModule());
  builder.RegisterAssemblyTypes(typeof(CommerceContext).Assembly)
    .Where(t => t.Name.EndsWith("Context"))
    .InstancePerRequest();
  builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
    .Where(t => t.Name.EndsWith("Service"))
    .AsImplementedInterfaces()
    .InstancePerRequest();
  var container = builder.Build();
  DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
  GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

对该方法的每个调用都是来自不同域的Web API。我几乎卡住了。我自己没有经历过错误,但是我可以在服务器日志中看到它。我唯一能想到的可能性是一次开放的连接太多,这真的很糟糕,因为该网站当前没有很多用户。我是否应该以不同的方式注册我的dbcontext,有多个或完全不同的东西。

不同的错误:

System.Web.HttpUnhandledException (0x80004005): The underlying provider failed on Open. ---> System.Data.Entity.Core.EntityException: The underlying provider failed on Open. ---> System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.
   at System.Data.ProviderBase.DbConnectionClosedConnecting.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action`2 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.Open(DbConnection connection, DbInterceptionContext interceptionContext)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.EntityClient.EntityConnection.Open()
   --- End of inner exception stack trace ---
   at System.Data.Entity.Core.EntityClient.EntityConnection.Open()
   at System.Data.Entity.Core.Objects.ObjectContext.EnsureConnection(Boolean shouldMonitorTransactions)
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at #.UserTokenService.Update(UserToken token) in E:TeamCitybuildAgentwork38cf8b2b27f5e099src#UserTokenService.cs:line 79
System.Web.HttpUnhandledException (0x80004005): An error occurred while starting a transaction on the provider connection. See the inner exception for details. ---> System.Data.Entity.Core.EntityException: An error occurred while starting a transaction on the provider connection. See the inner exception for details. ---> System.InvalidOperationException: SqlConnection does not support parallel transactions.
   at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName, Boolean shouldReconnect)
   at System.Data.SqlClient.SqlConnection.BeginTransaction(IsolationLevel iso, String transactionName)
   at System.Data.SqlClient.SqlConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.BeginTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.EntityClient.EntityConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   --- End of inner exception stack trace ---
   at System.Data.Entity.Core.EntityClient.EntityConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   at System.Data.Entity.Core.EntityClient.EntityConnection.BeginTransaction()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at #.UserTokenService.Update(UserToken token) in E:TeamCitybuildAgentwork38cf8b2b27f5e099src#UserTokenService.cs:line 79

我发现我的问题是由我为验证身份验证令牌验证的Webapi ActionFilter属性引起的。

在此属性中,我将我的UserTokenService存储在私有变量中,从依赖项索尔佛获得服务。

这是一个非常糟糕的解决方案,因为WebApi ActionFilter属性正在缓存,因此基本上我的服务正在通过使用该属性的每个API请求共享。

解决方案是使用httprequestmessage在Onationexecuting((中提供的范围:

    var scope = actionContext.Request.GetDependencyScope();
    var tokenService = scope.GetService(typeof(IUserTokenService)) as IUserTokenService;

一个更好的解决方案是使用AUTOFACS实现此处描述的ActionFilterAttribute:

http://autofaccn.readthedocs.io/en/latest/integration/webapi.html#provide-filters-filters-via-dependenty-Inpoction

这使控制器注入您的动作窗。但是在我的情况下,是需要范围服务的更好选择。

最新更新