MVC3 Action Filter Using Database (EF 4.1 DBContext, Ninject)

巧了我就是萌 提交于 2019-12-01 00:41:04

I'd do authentication in Application_AuthenticateRequest and authorization in your attribute using Thread.CurrentPrincipal, but your method should work too. You just need to count with fact that DbContext will be different for each request but your attribute won't. Something like this should do the trick (I'm assuming you are using 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);
    }
}

I have been battling with this for a while and finally solved my problem. So here is my solution in the hope it may help someone else.

The setup: 1. I have an MVC3 project, a custom action filter that accesses the db using EF5 via a business service. 2. I use Unity and unity.MVC to resolve my dependencies on a per request basis. 3. I use property injection into my custom Action filter, as it has a parameterless constructor.

The result. Dependency injection works correctly for all the services used by actions, my EF DbContext is correctly disposed of at the end of each request.

The Problem Although my property dependency is resolved in my custom action filter, it contains a stale instance of my DbContext (e.g. it seems to have been cached from the previous request)

As mentioned in previous posts, MVC3 is more aggressive with filter caching and the state of a filter cannot be relied on. So the suggestion was to resolve the dependency in the OnActionExecuting method. So I removed my injected property and did just that called resolve on my unity container. However I still got a stale version of the DbContext. Any changes in the DB were correctly queried in my main actions, but the custom action filter didn’t pick them up.

The solution. Unity.MVC Manages per-request lifetime by using child containers and disposing these at the end of each request. By resolving my dependency’s in the action filter from my unity container I was resolving from the parent container which is not disposed of on each request.

So rather than

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

I used this to obtain an instance of the child container rather than parent.

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

I'm sure there must be a clean way to achive the same result, so please add suggestions.

Ok slight refinement to allow my unit test to inject a mock of the service. 1. remove the dependency resolve from the the OnActionexecuting and add two constructors.

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

and

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

Now the constructor resolves your service and stores it as a private readonly. This can now be consumed in your OnActionExecutng function. Unit tests can now call the second constructor and inject a mock.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!