2

我有一个使用 Entity Framework 4.1 和 Ninject 的 MVC3 应用程序。它使用标准存储库模式,该模式在 PerRequest 的基础上接受 IUnitOfWork/DbContext(来自 Ninject)。

该网站在单用户测试中运行良好。我们最近开始对 2 个以上的用户进行一些并发负载测试,并开始在某些请求中出现此错误:

连接未关闭。连接的当前状态是正在连接。

System.Data.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.DbConnectionBusy.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
--- End of inner exception stack trace ---
at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
at System.Data.EntityClient.EntityConnection.Open()
at System.Data.Objects.ObjectContext.EnsureConnection()
at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Store.Security.StoreSecurityService.GetUserGroupRoles(String username) in C:\Dev\Store2\Trunk\Store.Security\StoreSecurityService.cs:line 57
at Store.Security.StoreSecurityService.GetRolesForUser(String username) in C:\Dev\Store2\Trunk\Store.Security\StoreSecurityService.cs:line 23
at Store.Security.StoreRoleProvider.GetRolesForUser(String username) in C:\Dev\Store2\Trunk\Store.Security\StoreRoleProvider.cs:line 37
at System.Web.Security.RolePrincipal.IsInRole(String role)
at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
at System.Web.Mvc.AuthorizeAttribute.AuthorizeCore(HttpContextBase httpContext)
at System.Web.Mvc.AuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext)
at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.<BeginProcessRequest>b__5()
at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.<MakeVoidDelegate>b__0()
at System.Web.Mvc.MvcHandler.<>c__DisplayClasse.<EndProcessRequest>b__d()
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

我想知道这个问题是否是由实现 MVC RoleProvider 类的自定义 RoleProvider 引起的,该类需要访问 DbContext 才能调用数据库。

public class StoreRoleProvider : RoleProvider
{
    public IStoreSecurityService StoreSecurityService { get; set; }

    public StoreRoleProvider()
    {
        StoreSecurityService = DependencyResolver.Current.GetService(typeof(IStoreSecurityService));
    }

    public override string[] GetRolesForUser(string username)
    {
        return StoreSecurityService.GetRolesForUser(username);
    }
}

最初,我们解析了一个 IStoreSecurityService 实例,该实例中注入了一个 DbContext (PerRequest),但我知道 RoleProvider 仅在应用程序启动时创建一次,因此 DbContext 将在请求结束时被处理掉。

我在构造函数中尝试了一个特定的实例,如下所示:

public StoreRoleProvider()
{
   StoreSecurityService = new StoreSecurityService(new DbContext());
}

但这会产生类似的错误。

抛出错误的 linq 查询并不是特别困难......

public IEnumerable<string> GetRolesForUser(string username)
{
    var roles = (from user in _dbContext.Set<User>()
                 join userRole in _dbContext.Set<UserRole>() 
                     on user.Id equals userRole.IserId
                 join role in _dbContext.Set<Role>() 
                     on userRole.RoleId equals role.Id
                 where user.UserName == username && !userRole.IsDeleted 
                 select role.Name).ToList<string>();
    return roles;
}

我不明白为什么连接状态变化如此之大。

任何想法将不胜感激:)

4

1 回答 1

3

经过多次测试,我想我已经解决了这个问题。

由于某种原因,RoleProvider 不喜欢使用 Singleton DbContext。

因此,作为一个不整洁的修复,我在 StoreSecurityService 上有两个构造函数。一个构造函数接收 IUnitOfWork(其中包含 DbContext),这是使用 Ninject 以正常方式解析的。

第二个构造函数没有参数。在代码内部,每次 linq 查询需要 DbContext 时,它都会直接调用 ninject 来解析当前的 IUnitOfWork 实例(即 PerRequest)。它为此上下文创建一个局部变量,然后照常使用它。

在类中包含 DependencyResolver 会使单元测试变得更加困难,因此我可以将此 GetDbContext 方法重构为一个单独的类以使其更加透明。

public class SolvencySecurityService : ISolvencySecurityService
{
    private readonly IUnitOfWork _privateContext;

    private DbContext GetDbContext()
    {
        if (_privateContext != null)
        return _privateContext;

        return DependencyResolver.Current.GetService<IUnitOfWork>();
    }

    public StoreSecurityService()
    {

    }

    public StoreSecurityService(IUnitOfWork unitOfWork)
    {
        _privateContext = unitOfWork;
    }
}
于 2012-11-12T10:47:08.417 回答