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

2

2

I'm trying to setup an 'Authorization' Filter on an Action, creating my own ActionFilterAttribute where I do a database lookup to determine if a user has access to a certain resource.

On my class inheriting from ActionFilterAttribute, I have created an Injected(Ninject) property to hold the service that I am using for the database access. I have a parameterless constructor so that I can use this as an attribute on my actions. In the 'OnActionExecuting' Method, I am able to gain access to the Injected property (it's not null), but the base DBCotext that it is using is closed.

This working fine, up until the RTM of MVC3, where the Release Notes stated:

Breaking Changes: In previous versions of ASP.NET MVC, action filters are create per request except in a few cases. This behavior was never a guaranteed behavior but merely an implementation detail and the contract for filters was to consider them stateless. In ASP.NET MVC 3, filters are cached more aggressively. Therefore, any custom action filters which improperly store instance state might be broken.

The first time I use this filter, it works as expected, but if I refresh the page or another user access this filter, I get the error:

The operation cannot be completed because the DbContext has been disposed.

which is what I guess I should expect given the breaking changes notes.

My question is this, what would be the preferred/recommended way of accomplishing what I need to do? Should this be in an ActionFilterAttribute, or should this 'authorization' be done somewhere else?

Obsolesce answered 22/4, 2011 at 18:23 Comment(1)
Have you figured anything out on this...having same issue.Imphal
R
3

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);
    }
}
Reifel answered 22/4, 2011 at 18:58 Comment(3)
That's exactly the approach that I went with, but using that approach defies the purpose of using 'Injected' Properties or Constructors then, doesn't it? Is it a good approach to use DependencyResolver whenever I need to gain access to one of my Services?Obsolesce
The purpose of using dependency injection is so you can dynamically resolve dependencies - usually for whole instance, but in this case just for one method - thats how I see it (but I'm not an expert on DI, so I might be wrong). DependencyResolver is mvc specific, so using it in mvc context is in my opinion valid, but if you want something fancy map IServiceProvider to your Kernel instance and inject IServiceProvider to the attribute and use that instead of DependencyResolver.Trudytrue
I had the same issue and ended up using the DependencyResolver. To my knowledge, constructor injection wouldn't work if your DI container is managing your DbContext per-request, since the filter is instantiated outside the request. (I'm using Ninject and .InRequestScope())Ancylostomiasis
R
0

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.

Rogers answered 10/4, 2014 at 8:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.