2016-08-31 12 views
0

В настоящее время у меня есть следующий ActionFilterAttribute в моем проекте MVC. Он отлично работает для первого запроса, но последующие запросы возвращают сообщение, которое размещается DbContext.MVC ActionFilterAttribute DbContext Disposed Solutions

public class PermissionFilter : ActionFilterAttribute 
{ 
    private readonly ApplicationGroupManager _groupManager = new ApplicationGroupManager(); 
    private readonly ActionPermissionManager _permissionManager = new ActionPermissionManager(); 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var request = filterContext.HttpContext.Request; 
     var response = filterContext.HttpContext.Response; 
     if (request.IsAjaxRequest()) 
     { 
      #region Preventing caching of ajax request in IE browser 

      response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1)); 
      response.Cache.SetValidUntilExpires(false); 
      response.Cache.SetCacheability(HttpCacheability.NoCache); 
      response.Cache.SetNoStore(); 

      #endregion Preventing caching of ajax request in IE browser 
     } 
     var currentAreaName = filterContext.RequestContext.RouteData.DataTokens["area"]; 
     var currentActionName = filterContext.ActionDescriptor.ActionName; 
     var currentControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; 
     var userId = HttpContext.Current.User.Identity.GetUserId<int>(); 

     if (!_groupManager.UserHasAdministratorAccess(userId)) 
     { 
      if (!_permissionManager.HasPermission((currentAreaName == null ? String.Empty : currentAreaName.ToString()), currentControllerName, currentActionName, userId)) 
      { 
       filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Account" }, { "action", "Login" } }); 
      } 
     } 

     base.OnActionExecuting(filterContext); 
    } 
} 

Я прочитал достаточно, чтобы понять, что это проблема со следующим изменением, которое было введено в MVC3

отличий: В предыдущих версиях ASP.NET MVC, фильтры действий которые создают по запросу, за исключением нескольких случаев. Такое поведение никогда не было гарантированным поведением, а просто детальностью реализации и контрактом для фильтров было считать их безстоящими. В ASP.NET MVC 3, фильтры кэшируются более агрессивно. Поэтому любое настраиваемое действие фильтров, которые неправильно сохраняют состояние экземпляра, может быть нарушено.

Я не уверен, как лучше всего решить эту проблему. Я подумал о переносе моих двух личных полей readonly в часть OnActionExecuting, которая, как я считаю, решит проблему, но я беспокоюсь о многопоточности и, если будут проблемы с этой реализацией.

Кажется, многие люди обошли эту проблему, используя Castle Windsor или Ninject, но они находятся за пределами моего уровня знаний и даже после прохождения урока Windsor (https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md). Я не могу понять, что именно мне нужно делать.

+1

В вашем случае использование Castle Windsor или Ninject похоже на попытку ударить по булавке молотом Тора. –

ответ

0

Проблема с вашими двумя полями, которые вы инициализацию и сохранить:

private readonly ApplicationGroupManager _groupManager = new ApplicationGroupManager(); 
private readonly ActionPermissionManager _permissionManager = new ActionPermissionManager(); 

Предположительно, по крайней мере один из них имеет экземпляр DbContext, который также инициализирован в конструкторе или в поле/собственности инициализаторе. Я, очевидно, не знаю внутренней работы этих классов, но не похоже, что они используются другими способами. Они действительно должны быть объявлены внутри метода вместо полей классов:

public override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    ApplicationGroupManager groupManager = new ApplicationGroupManager(); 
    ActionPermissionManager permissionManager = new ActionPermissionManager(); 

    // The rest of your code here 
} 

Это также устранит ошибку, которую вы видите. Поскольку экземпляры Manager (и их члены DbContext) повторно инициализированы для каждого запроса, вам не нужно беспокоиться о коде, пытающемся повторно использовать удаленные экземпляры.

+0

Это решение, на которое я склоняюсь, как я сказал в своем вопросе. Однако есть ли какие-либо проблемы с переменными, так как существует только один экземпляр ActionFilterAttribute для каждого декорированного метода? Могут ли два одновременных запроса к одному и тому же методу привести к проблемам с этими переменными? – Brad

+0

Это действительно вопрос для вас ... они твои классы. Если они сохраняют статическое или глобальное состояние внутри, то да - это может быть проблемой. Пока ваши классы хорошо разработаны и явно объявляют зависимости, тогда у вас не должно быть никаких проблем. –

0

Альтернативой является создание класса фильтра прокси, который будет создавать экземпляр и связывать фактический фильтр.

public class PermissionFilterProxy : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(ActionExecutingContext filterContext) 
     { 
      // create and wire the actual filter 
      // so in this way its lifetime is handled by us 
      PermissionFilter permissionFilter = new PermissionFilter(); 
      permissionFilter.OnActionExecuting(filterContext); 
      base.OnActionExecuting(filterContext); 

     } 
    } 

Вместо PermissionFilter вы настроите (добавить) в настоящее время этот прокси-сервер.

+0

Это интересное решение ... Спасибо. – Brad

+0

Обратите внимание, что это все еще приводит к повторной инициализации ваших классов менеджера по запросу.Это фактически тот же эффект, что и мое решение ниже, но это решение лучше, если вы не можете изменить базовый фильтр (или вы просто действительно не хотите по какой-то причине). –