我试图在一个动作上设置一个'授权'过滤器,创建我自己的ActionFilterAttribute,在那里我做一个数据库查找,以确定用户是否有权访问某个资源。
在继承ActionFilterAttribute的类上,我创建了一个inject (Ninject)属性来保存我用于数据库访问的服务。我有一个无参数的构造函数,这样我就可以将其用作操作的属性。在' onactionexecution '方法中,我能够访问注入属性(它不是null),但它正在使用的基本dbcontext是关闭的。
这个工作很好,直到MVC3的RTM,其中发布说明说:
<打破变化/strong>:在以前的ASP版本中。. NET MVC中,动作过滤器是每创建一个请求,少数情况除外。这行为从未得到保证行为,而仅仅是一个实现过滤器的详细信息和合同认为他们是无国籍的。在ASP。. NET MVC 3,过滤器缓存更多积极。因此,任何习俗操作过滤器不正确地存储实例状态可能被破坏。
我第一次使用这个过滤器时,它按预期工作,但是如果我刷新页面或其他用户访问这个过滤器,我得到错误:
操作无法完成因为DbContext已经被处理。
这是我想我应该期待的,因为打破了更改笔记。
我的问题是,完成我需要做的事情的首选/推荐方法是什么?这应该在ActionFilterAttribute中,还是应该在其他地方完成这个"授权"?
我会在Application_AuthenticateRequest
中进行身份验证,并使用Thread.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. 我有一个MVC3项目,一个使用EF5通过业务服务访问数据库的自定义动作过滤器。2. 我使用统一和统一。MVC来解决我对每个请求的依赖。3.我在自定义的Action过滤器中使用了属性注入,因为它有一个无参数的构造函数。
结果。依赖注入对操作使用的所有服务都正确工作,我的EF DbContext在每个请求结束时被正确处理。
这个问题虽然我的属性依赖在我的自定义动作过滤器中解决了,但它包含了我的DbContext的一个过时的实例(例如,它似乎已经从以前的请求中缓存)
正如在之前的文章中提到的,MVC3在过滤器缓存方面更具侵略性,并且不能依赖过滤器的状态。所以建议是解决onactionexecution方法中的依赖。所以我删除了我的注入属性并在我的unity容器上做了那个叫做resolve的事情。然而,我仍然得到了一个旧版本的DbContext。在我的主操作中正确地查询了DB中的任何更改,但是自定义操作过滤器没有将它们拾取。
解决方案。团结。MVC通过使用子容器并在每个请求结束时处理它们来管理每个请求的生命周期。通过从我的统一容器中解析我在动作过滤器中的依赖项,我是从父容器中解析的,父容器在每次请求时都不会被处理。
所以与其
IoC.Instance.CurrentContainer.Resolve<IService>();
我用它来获取子容器的实例,而不是父容器的实例。
var childContainer = HttpContext.Current.Items["perRequestContainer"] as IUnityContainer;
var service = childContainer.Resolve<IServcie>();
我相信一定有一个干净的方法来达到同样的结果,所以请添加建议。
好的,稍微改进一下,允许我的单元测试注入服务的模拟。1. 从onactionexecution中移除依赖解析,并添加两个构造函数。
public MyCustomActionfilter() : this(((IUnityContainer)HttpContext.Current.Items["perRequestContainer"].Resolve<IService>())
和
public MyCustomActionfilter(IService service)
{
this.service = service;
}
现在构造函数解析了您的服务并将其存储为私有只读。现在可以在onactionexecution函数中使用。单元测试现在可以调用第二个构造函数并注入模拟。