Sunday, March 20, 2011

InjectableFilterAttribute never hits the Filter

On my base controller I have placed the Logger attribute. This LoggerAttribute looks like this:

public class LoggerAttribute: InjectableFilterAttribute
{
    public override Type FilterType
    {
        get { return typeof (LoggerActionFilter); }
    }
}

The ctor on this loggerattribute gets hit, but the FilterType getter not.

The relevant part of the filter itself looks like this:

public class LoggerActionFilter: IActionFilter
{
    private readonly ILoggerService logger;

    public LoggerActionFilter (ILoggerService logger)
    {
        this.logger = logger;
    }
    <IActionFilter Implementeation>
}

The filter's ctor never gets hit either.

For the wiring of my services and instantiation of servicelocator check here
The registration of the ILoggerService can be found here

What am I missing?

From stackoverflow
  • This ought to work automatically provided that the correct ControllerFactory is configured for the application.

    As far as I can tell, this must be an instance of TurbineControllerFactory or a derived class. The TurbineControllerFactory sets up the TurbineActionInvoker which is responsible for locating the correct filters.

    Note that if you register a custom IControllerFactory with your DI Container (Service Locator in Turbine terminology), this IControllerFactory type will be used instead, and if this doesn't derive from TurbineControllerFactory, it will not assign an instance of TurbineActionInvoker to the created Controller - which again means that your InjectableFilterAttribute is never invoked.

    The intended way to configure a Turbine application is to define a custom application class that derives from TurbineApplication.

    As an example, here's the entire contents of a Turbine-configured Global.asax:

    <%@ Application Codebehind="Global.asax.cs" Inherits="MyApplication" Language="C#" %>
    

    However, note that there isn't any Global.asax.cs.

    The MyApplication class must derive from TurbineApplication and correctly configure the DI Container. Here's one way to do it:

    public class MyApplication : TurbineApplication
    {
        static MyApplication()
        {
            ServiceLocatorManager.SetLocatorProvider(() => new WindsorServiceLocator());
        }
    }
    

    Obviously, you can replace the WindsorServiceLocator with another DI Container if you use a different one.

    borisCallens : I'm not sure I'm understanding this correctly. You say there is no global.asax.cs, yet you say the Myapplication code should exist. What I did in my app is have a MyMVCApplication: TurbineApplication and have that code in my global.asax.cs. Is that correct?
    borisCallens : Please also note that it's only the attributes that have problems. Controls and other classes work just fine.
    Mark Seemann : AFAICT, it doesn't matter in which file you define your MyMVCApplication class. What matters is that it derives from TurbineApplication. You don't have to have it in global.asax.cs, but you can if you'd like. Based on what I know about Turbine it makes sense that only the attributes are affected if the TurbineActionInvoker isn't wired up correctly.
    borisCallens : You can find the code for my TurbinApplication here (also updated OP): http://pastebin.com/f529106ea
    Mark Seemann : Your MvcApplication looks okay, but have you specified the correct Inherits attribute value in Global.asax as described above? It would have to say Inherits="MvcApplication".
    borisCallens : It was correct by default as I've merely taken the default structure and replaced the implementation of th MvcApplication class in global.asax.cs
    Mark Seemann : Can you step into the code via the debugger and check whether an appropriate Controller has an instance of TurbineActionInvoker assigned?
    borisCallens : Yep, it's an instance of MvcTurbine.Web.Controllers.TurbineActionInvoker
    Mark Seemann : I suspect that the problem could be because the ILogger service isn't registered - at least, it isn't registered in your container setup. I think you need to register it either while setting up the container in global asax, or as an IServiceRegistration as demonstrated in the FilterInjection sample.
    borisCallens : The logger is working in non-attributes, so I think the registration is ok. I'll add the logging registration to OP as reference.
    borisCallens : Also: thanks for sticking with my question through these days. Although I don't want to mark this as answer (you can't change the answer on a bountied question after end of bounty), I would like to see the reward go to you. Any idea how can be worked out?
    Mark Seemann : I must admit that an explanation and solution currently eludes me. Sanity check: Does the FilterInjection sample work in your environment? Further troubleshooting: In the debugger, can you verify whehter IActionFilter is registered in the Container?
    Mark Seemann : One further idea: What happens if you put the attribute on a concrete Controller instead of on a base Controller?
    borisCallens : The sample runs as expected. To be sure I switcherood the turbine binaries used in my app with the one from the sample. I also tried putting the attribute on a concrete class. I'm not sure how to verify the registration of IActionFilter. As mentioned in OP, the LoggerAttribute: InjectableFilterAttribute gets constructed, but the FilterType getter doesn't get hit for some reason. For now I've just put a static ref in the LoggerAttribute class and made it implement IActionFilter directly. It does the job, but works against all the work I did to make everything injectable.
    Mark Seemann : That the Attribute's constructor is hit doesn't tell you anything, because that happens as part of the type's static initialization. IIRC, all attribute constructors are invoked when the type is initialized.
    Mark Seemann : Can you try to add the Turbine source projects to your application instead of referencing the binaries? This will allow you to easily step into the Turbine code and see if everything gets wired correctly. TurbineActionInvoke.GetFilters is the interesting method there... Be aware that this registration only happens once per AppDomain, so you may have to shut down Cassini or IIS before you step into the code. `registeredFilters.ActionFilters` ought to contain your LoggerActionFilter, but it probably doesn't...
    borisCallens : Linking in was the very first thing I tried, but I ran in some new troubles as you can read here: http://stackoverflow.com/questions/1952964/built-with-optimizations-enabled-or-without-debug-information
    Mark Seemann : I'm more or less out of ideas... It sounds like something in your setup is sick. Can you reproduce the issue on a different machine?
    borisCallens : Don't know, I can't try at this moment. I will try when my home rig is up and running again. Thanks for your collaboration. I got the feeling that it's not something in this part of the code, so I'm going to leave it as is for now. As the definition of the accepted answer is the answer that the author of the question thinks was the most helpful, I think this answer should get the rep :)
    Mark Seemann : Thanks a lot - sorry I couldn't be of more help.
  • Hi Boris,

    I have a couple of questions for you:

    • Which version of MVC are you using? v1 or v2? if v2 is it the RC or beta2?
    • Have you seen the Filter Injection sample?
    borisCallens : The projects I'm referring to use MVC v1. I also had a look at the injection sample and basically mimicked the code used there, but to no avail. I tried linking in the source code so I could follow why it isn't working out, but I had some difficulties there too as you can follow in this question: http://stackoverflow.com/questions/1952964/built-with-optimizations-enabled-or-without-debug-information
    Javier Lozano : is your project a sample project or is it work related? I wouldn't mind taking a look at it. Perhaps we can skype it and see what's wrong. Then post the answer back to this question. Thoughts?
    borisCallens : Well, I would love to, but it's a work related project. My boss is frantic about the non-disclosure module and seeing how these are my last weeks at this firm I don't want to do anything retarded ;) Once the 3.5 turbine libs for mvc2 hit the air, I will add a similar logging attribute to my pet project too though. You can find said project at https://beek.googlecode.com/svn/trunk
    borisCallens : That would be http://beek.googlecode.com/svn/trunk/ for anonymous
    borisCallens : Added the logging attrib, but as currently the mvcTurbine libs aren't working with MVC2 it's not compilable.

0 comments:

Post a Comment