2

EntityFramework4.1とNinjectを使用するMVC3アプリケーションがあります。これは、PerRequestベースで(Ninjectからの)IUnitOfWork/DbContextを受け入れる標準のリポジトリパターンを使用します。

このWebサイトは、シングルユーザーテストで正常に機能しています。最近、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)

この問題の原因は、データベースを呼び出すためにDbContextにアクセスする必要があるMVCRoleProviderクラスを実装するカスタム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);
    }
}

当初、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;
}

なぜ接続状態がそんなに変化しているのかわかりません。

どんなアイデアでもありがたいです:)

4

1 に答える 1

3

多くのテストを行った結果、問題は修正されたと思います。

何らかの理由で、RoleProviderはシングルトンDbContextの使用を好みません。

少し厄介な修正として、StoreSecurityServiceに2つのコンストラクターがあります。1つのコンストラクターは、通常の方法でNinjectを使用して解決されるように、IUnitOfWork(DbContextを含む)を受け取ります。

2番目のコンストラクターにはパラメーターがありません。コード内では、linqクエリがDbContextを必要とするたびに、現在のIUnitOfWorkインスタンス(PerRequest)を解決するためにninjectを直接呼び出します。このコンテキストのローカル変数を作成し、通常どおりに使用します。

クラス内に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 に答える