Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

We use a custom AuthorizeAttribute to handle a few aspects of user authorization. I need to add access to the database to check a value during authorization. This project uses the Repository pattern and Repos are all instantiated in controller constructors via StructureMap.

Unfortunately, it appears there's no way to use constructor injection with filters. I found an article by Jimmy Bogard (http://lostechies.com/jimmybogard/2010/05/03/dependency-injection-in-asp-net-mvc-filters/) explaining how to use property injection to handle this scenario. Basically, Jimmy's code intercepts the GetFilters method in ControllerActionInvoker and runs BuildUp on each filter to populate the properties. This was just what I needed, so I added the following class -

public class InjectingActionInvoker : ControllerActionInvoker
{
    private readonly IContainer _container;

    public InjectingActionInvoker(IContainer container)
    {
        _container = container;
    }

    protected override FilterInfo GetFilters(
        ControllerContext controllerContext, 
        ActionDescriptor actionDescriptor)
    {
        var info = base.GetFilters(controllerContext, actionDescriptor);

        info.AuthorizationFilters.ForEach(_container.BuildUp);
        info.ActionFilters.ForEach(_container.BuildUp);
        info.ResultFilters.ForEach(_container.BuildUp);
        info.ExceptionFilters.ForEach(_container.BuildUp);

        return info;
    }
}

And then wired it into StructureMap with these lines -

For<IActionInvoker>().Use<InjectingActionInvoker>();
For<ITempDataProvider>().Use<SessionStateTempDataProvider>();

Policies.SetAllProperties(c =>
{
    c.OfType<IActionInvoker>();
    c.OfType<ITempDataProvider>();
    c.WithAnyTypeFromNamespaceContainingType<UserProfileRepository>();
});

And finally, I added the public property to my custom AuthorizeAttribute class -

[SetterProperty]
public UserProfileRepository User { get; set; }

When I run the project and access a secure page, the AuthorizeCore code is hit twice. The first time, my property is set and working properly. However, the second call fails because the property is null. I set a breakpoint in the GetFilters method, and it's only being hit the first time. Unfortunately, I just don't have a strong enough understanding of StructureMap or Filters to know exactly where this is going sideways.

Below are the call stacks, in case that is useful for anyone -

Call #1

AppName.dll!AppName.Filters.SiteAuthorizeAttribute.AuthorizeCore(System.Web.HttpContextBase httpContext) Line 78    C#
[External Code] 
AppName.dll!AppName.Filters.SiteAuthorizeAttribute.OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) Line 31   C#
[External Code]

Call #2

AppName.dll!AppName.Filters.SiteAuthorizeAttribute.AuthorizeCore(System.Web.HttpContextBase httpContext) Line 69    C#
[External Code] 
AppName.dll!AppName.Filters.SiteAuthorizeAttribute.OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) Line 31   C#
[External Code] 
App_Web_1fnmflat.dll!ASP._Page_Views_Shared__Menu_cshtml.Execute() Line 2   C#
[External Code] 
App_Web_1fnmflat.dll!ASP._Page_Views_Shared__Layout_cshtml.Execute() Line 51    C#
[External Code] 

Any StructureMap masters care to share some wisdom? Thanks in advance for any help!

Edit: Here's the code for the _Menu.cshtml file -

@(Html.Kendo().Menu()
      .Name("Menu")
      .Items(items =>
      {
          items.Add().Text("My Dashboard").Action("Dashboard", "Home");
          items.Add().Text("My Account").Action("Edit", "Account");
          items.Add().Text("Purchase/Renew").Action("Index", "Purchase");
          items.Add().Text("Administration")
              .Items(children =>
              {
                  children.Add().Text("Accounts").Action("Index", "UserProfile");
                  children.Add().Text("Coupons").Action("Index", "Coupon");
              });
          items.Add().Text("Logout").Action("Logout", "Logon");
      })
      )

Thanks to some prompting from NightOwl888, I have isolated the issue to the Kendo Menu call. If I place a breakpoint on the final line of _Menu.cshtml and step into, I see DoGetInstance called for my HomeController. As soon as that is completed, OnAuthorization is fired for the second time and my repo property is null.

Anyone know what I'm missing here?

share|improve this question
    
I don't think you will find an answer unless you can explain why your authorize attribute is being called twice, and who is instantiating it the second time. Is your GetFilters method being called twice? –  NightOwl888 Dec 8 '14 at 18:00
    
Sorry, the 2nd call is coming from the rendering of a partial named _Menu.cshtml inside my _Layout.cshtml file (you can see this in the call stacks). –  Phil Figgins Dec 8 '14 at 20:09
    
And, no, GetFilters is only being called one time (as stated above). It feels like the class is being cached but the cached version is being stored before the BuildUp has happened? I don't know. –  Phil Figgins Dec 8 '14 at 20:11

3 Answers 3

Since you know that GetFilters is not being called during the second call and the first instance of AuthorizeAttribute is working, then this clearly isn't an issue with your DI setup. If the AuthorizeAttribute instance were being cached, then all of the dependencies would be cached as well, so that isn't it either.

The issue boils down to the fact that your menu isn't calling the FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor); method before using the AuthorizeAttribute. I can only guess as to why though because you didn't provide the code for your menu or its partial method.

You could try overriding ControllerActionInvoker.InvokeAuthorizationFilters instead.

share|improve this answer
    
Thanks. I tried InvokeAuthorizationFilters, but same result. I've added more details in an edit above. –  Phil Figgins Dec 9 '14 at 15:48

I too was facing the same issue and was trying different methods, finally this worked for me after removing [AllowMultiple = true] and now AuthorizeCore is called once. I am curious how this is linked to AuthorizeCore.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CompanyAuthorizeAttribute : AuthorizeAttribute
{}

before it was

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class CompanyAuthorizeAttribute : AuthorizeAttribute
{}
share|improve this answer
    
Thanks, Mohan. Unfortuantely your fix didn't work for me. Hopefully it does for someone else stumbling on this page! –  Phil Figgins Dec 22 '14 at 20:38
    
I got you question wrong. I was working on MVC5 proj and installed StructureMap.MVC5 @GitHub .This gives you basic IOC. To implement IOC for Filters I did below modifications. ActionInvoker expects container obj. public static IContainer Initialize() { var _con = new Container(c => c.AddRegistry<DefaultRegistry>()); _con.Configure(c => c.AddRegistry(new ActionFilterRegistry(_con))); return _con; } public class ActionFilterRegistry: Registry { public ActionFilterRegistry(IContainer con) { For<IFilterProvider>().Use(new InjectingActionInvoker(con)); Policies.SetAllProperties(....); } } –  Mohan Katari Dec 29 '14 at 13:36
up vote 0 down vote accepted

Sadly, I haven't been able to track down what is happening. So here's how I've "fixed" the issue for now -

    [SetterProperty]
    public UserProfileRepository User
    {
        get { return _user ?? DependencyResolver.Current.GetService<UserProfileRepository>(); }
        set { _user = value; }
    }

When the injection works the injected value is used, otherwise I use the DependencyResolver to do the dirty work by hand. Not pretty, but I have no other options right now.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.