我有一个使用Entity Framework 4.1和Ninject的MVC3应用程序。它使用标准的Repository模式,在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);
}
}
最初,我们解决了一个注入了DbContext的IStoreSecurityService实例(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 :(得分: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;
}
}