2

我有一個使用Entity Framework 4.1和Ninject的MVC3應用程序。它使用標準的Repository模式,它在PerRequest的基礎上接受IUnitOfWork/DbContext(來自Ninject)。單身DbContext的自定義RoleProvider可能與MVC3網站中的PerRequest DbContext衝突

該網站在單用戶測試中運行良好。我們最近開始與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類,它需要訪問的DbContext,使調用數據庫自​​定義RoleProvider造成我想知道。

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

我不明白爲什麼連接正在改變狀態這麼多。

任何想法,將感激地讚賞:)

+0

可能的欺騙:http://stackoverflow.com/questions/12677975/what-c​​ould-cause-many-data-access-exceptions-using-ef-code-first-in-a-custom-rol 。 –

+0

感謝您的鏈接。它是同樣的問題,但解決方案與我的問題不同。 –

回答

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