asp.net-mvc - 具有单例 DbContext 的自定义 RoleProvider 可能与 MVC3 网站中的 PerRequest DbContext 冲突

标签 asp.net-mvc entity-framework-4 database-connection dbcontext roleprovider

我有一个使用 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 的一个实例,其中注入(inject)了 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;
}

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

任何想法将不胜感激:)

最佳答案

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

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

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

第二个构造函数没有参数。在代码内部,每次 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;
    }
}

关于asp.net-mvc - 具有单例 DbContext 的自定义 RoleProvider 可能与 MVC3 网站中的 PerRequest DbContext 冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13314675/

相关文章:

php - 使用 PHP 和 mySQL 生成产品页面

c# - NHibernate MySqlException

python - 关闭 Snowflake DB 日志记录,同时仍将日志级别保持为 DEBUG

c# - 使用 jQuery 进行 asp.net mvc 分页有哪些可用选项?

c# - .net MVC 使用数据库的简单成员身份验证

asp.net-mvc - JSONIgnore 类上的数据注释

entity-framework-4 - 从数据库创建模型时更改表名称中的实体名称/poco 类名称

c# - ToList() 与 EF4 的高锁争用和低性能

c# - 如何在 Web Api 中访问用户身份

asp.net-mvc - 将 ASP 网站部署到 IIS 后,如何修复与 Access 数据库丢失的连接?