使用数据库的MVC3动作过滤器(EF 4.1 DBContext,Ninject)

时间:2011-04-22 18:23:10

标签: asp.net-mvc-3 ninject entity-framework-4.1

我正在尝试在Action上设置'Authorization'过滤器,创建我自己的ActionFilterAttribute,在那里我进行数据库查找以确定用户是否可以访问某个资源。

在继承自ActionFilterAttribute的类中,我创建了一个Injected(Ninject)属性来保存我用于数据库访问的服务。我有一个无参数构造函数,以便我可以将其用作我的操作的属性。在'OnActionExecuting'方法中,我能够访问Injected属性(它不是null),但它正在使用的基本DBCotext已关闭。

此工作正常,直到MVC3的RTM,发行说明中所述:

  

重大变化:       在以前版本的ASP.NET MVC中,将创建动作过滤器   除少数情况外请求。这个   行为从未得到保证   行为,但仅仅是一个实现   细节和过滤器合同   是认为他们无国籍。在   ASP.NET MVC 3,过滤器的缓存更多   积极。因此,任何习惯   不正确存储的动作过滤器   实例状态可能会被破坏。

我第一次使用此过滤器时,它按预期工作,但如果我刷新页面或其他用户访问此过滤器,我会收到错误:

  

无法完成操作   因为DbContext已经存在   地布置。

这是我想我应该期待给出的突破性变化笔记。

我的问题是,完成我需要做的事情的首选/推荐方法是什么?这应该在ActionFilterAttribute中,还是应该在其他地方完成“授权”?

2 个答案:

答案 0 :(得分:3)

我会使用Application_AuthenticateRequestThread.CurrentPrincipal和身份授权中进行身份验证,但您的方法也应该有效。你只需要计算DbContext对于每个请求都不同但你的属性不会。像这样的东西应该做的伎俩(我假设你使用DependencyResolver):

public class MyMightyAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var context = (DbContext)DependencyResolver.Current.GetService(typeof(DbContext))
        // authenticate, authorize, whatever
        base.OnActionExecuting(filterContext);
    }
}

答案 1 :(得分:0)

我一直在和这个争斗一段时间,最后解决了我的问题。所以这是我的解决方案,希望它可以帮助别人。

设置: 1.我有一个MVC3项目,一个自定义操作过滤器,通过业务服务使用EF5访问数据库。 2.我使用Unity和unity.MVC来解决每个请求的依赖关系。 3.我将属性注入用于我的自定义Action过滤器,因为它有一个无参数构造函数。

结果。 依赖注入对于操作使用的所有服务都能正常工作,我的EF DbContext在每个请求结束时都被正确处理。

问题 虽然我的自定义操作过滤器解决了我的属性依赖关系,但它包含我的DbContext的陈旧实例(例如,它似乎已从先前的请求缓存)

正如之前的帖子中所提到的,MVC3在过滤缓存方面更具攻击性,并且不能依赖过滤器的状态。所以建议是解决OnActionExecuting方法中的依赖关系。所以我删除了我注入的属性,并在我的Unity容器上执行了所谓的解析。但是我仍然有一个DbContext的陈旧版本。在我的主要操作中正确查询了数据库中的任何更改,但自定义操作筛选器未选中它们。

解决方案。 Unity.MVC使用子容器管理每个请求的生命周期,并在每个请求结束时处理这些容器。通过从我的统一容器中解析动作过滤器中的依赖关系,我从父容器中解析,而不是在每个请求中处理掉。

所以而不是

IoC.Instance.CurrentContainer.Resolve<IService>();

我用它来获取子容器的实例而不是父容器。

var childContainer = HttpContext.Current.Items["perRequestContainer"] as IUnityContainer;
var service = childContainer.Resolve<IServcie>();

我确定必须有一个干净的方法来获得相同的结果,所以请添加建议。

稍微改进一下,允许我的单元测试注入服务的模拟。 1.从OnActionexecuting中删除依赖关系解析并添加两个构造函数。

public MyCustomActionfilter() : this(((IUnityContainer)HttpContext.Current.Items["perRequestContainer"].Resolve<IService>())

public MyCustomActionfilter(IService service)
{
    this.service = service;
}

现在,构造函数会解析您的服务并将其作为私有只读存储。现在可以在OnActionExecutng函数中使用它。单元测试现在可以调用第二个构造函数并注入模拟。