MVC ActionFilterAttribute DbContext dispose解决方案

本文关键字:解决方案 dispose DbContext ActionFilterAttribute MVC | 更新日期: 2023-09-27 18:05:14

我目前有以下ActionFilterAttribute在我的MVC项目。对于第一个请求,它工作得很好,但是随后的请求返回一条消息,表示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;过滤器被更积极地缓存。因此,任何自定义操作不正确存储实例状态的过滤器可能会被破坏。

我不确定如何最好地解决这个问题。我考虑将我的两个私人只读字段移动到onactionexecution块,我相信会解决这个问题,但我担心多线程,如果有问题的实现。

似乎很多人都使用Castle Windsor或Ninject来解决这个问题,但这些都超出了我的专业水平,甚至在浏览了Windsor教程(https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md)之后,我也无法理解我到底需要做什么。

MVC ActionFilterAttribute DbContext dispose解决方案

问题出在您初始化并保留的两个字段上:

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

另一种选择是创建一个代理过滤器类,它将实例化并连接实际的过滤器。

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