MVC ActionFilterAttribute DbContext Disposed Solutions

时间:2016-08-31 19:42:09

标签: c# asp.net asp.net-mvc dbcontext actionfilterattribute

我目前在我的MVC项目中有以下ActionFilterAttribute。它适用于第一个请求,但后续请求会返回DbContext被释放的消息。

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中,   过滤器被更积极地缓存。因此,任何自定义操作   不正确地存储实例状态的过滤器可能会被破坏。

我不确定如何最好地解决此问题。我考虑将我的两个私有只读字段移动到OnActionExecuting部分,我认为这将解决问题,但我担心多线程以及是否存在该实现的问题。

似乎有很多人使用Castle Windsor或Ninject解决了这个问题,但这些都超出了我的专业水平,即使经过温莎教程(https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md),我也无法理解究竟是什么我需要做。

2 个答案:

答案 0 :(得分: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成员)已针对每个请求重新初始化,因此您不必担心代码会尝试重新使用已处置的实例。

答案 1 :(得分: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,您将配置(添加)此代理。