RegisterWebApiRequest and ActionFilterAttribute injection not working?

Feb 12, 2015 at 8:47 PM
Edited Feb 12, 2015 at 8:54 PM
Hi,

i am trying to create a logger per webapirequest.
Since i want this logger to contain request specific header data like request id, action name etc. i created a a filter inheriting ActionFilterAttribute.

the logger should be property injected, which is not.

the registration is as follows:

container.RegisterWebApiRequest(loggerFactory.Get);
container.RegisterInitializer<LoggingContextFilter>(handler => { handler.Logger = container.GetInstance<ILogger>(); });

am i missing some plumbing code here?
Coordinator
Feb 12, 2015 at 9:05 PM
Edited Feb 13, 2015 at 8:38 AM
Attributes are not created by the container but by the CLR. That makes them unsuited for use with Dependency Injection.

Web API contains an IFilterProvider abstraction that allows you to intercept any filter attribute that gets created. Simple Injector even contains some integration mechanism that makes use of this IFilterProvider to allow doing property injection on filter attributes.

I however advise against the use of this integration mechanism. This mechanism is only useful when injecting dependencies that are configured as singletons, because Web API caches filter attributes, making them effectively singletons as well. Injecting anything but singletons into those attributes will cause a captive dependency, which might lead to concurrency bugs. Because of these limitations, we will make this integration mechanism obsolete in a future release.

Instead, prefer making your attributes passive, as also described here, or fall back to a simple Service Locator approach, where you make the attribute a humble object. Here's an example of such humble object:
public class LoggingContextFilter : FilterAttribute
{
    public override Task OnActionExecutingAsync(HttpActionContext actionContext, 
        CancellationToken cancellationToken)
        var service = DependencyResolver.Current.GetService(typeof(IControllerLoggingService))
            as IControllerLoggingService;
        service.Execute();
        return TaskHelpers.Completed();
    }
}
With the humble object approach, the amount of logic in the attribute should be minimal; ideally just call to the dependency resolver to grab a service and calling the method on that service. That means that you should move all the logic out of that attribute and place it into this tailored service.
Feb 12, 2015 at 9:42 PM
Since i am using webapi the syntax is actually
GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof (ILogger)) as ILogger;
But i can't get it to work.

My goal is simple.
get a new logger on each wep api request.
set some properties on the logger early in the pipeline so that every resolved logger contains these properties ( i am using a filter).

with the filter i am getting an instance of the logger but later in the same request the intance is not the same (properties set in the filter are null).

Registration
container.RegisterWebApiRequest(loggerFactory.Get);
Filter
 public class LoggingContextFilter : ActionFilterAttribute
    {
        public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            var logger = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof (ILogger)) as ILogger;
            
            logger.SetContext(actionContext.Request.GetCorrelationId());
            return TaskHelpers.Completed();
        }
    }
Any ideas?
Coordinator
Feb 13, 2015 at 9:02 AM
Edited Feb 13, 2015 at 10:24 AM
Can you post the configuration for your ILogger?
Feb 13, 2015 at 10:34 AM
it is already up there.

Registration
container.RegisterWebApiRequest(loggerFactory.Get);
where the factory news up a ilogger instance. I am using the webapi extension!
Coordinator
Feb 13, 2015 at 12:23 PM
Your configuration is correct. The logger returned from loggerFactory.Get is cached for the duration of the web request, so I'm not sure what's going on here.
Feb 13, 2015 at 12:29 PM
One thing i have noticed is that on multiple requests the filter is fired only once.
maybe it is cached, maybe it is my imagination!
Coordinator
Feb 13, 2015 at 12:33 PM
The filter attribute is created just once (per action), because Web API caches filter attributes. So you can't do property injection on filter attributes. But your OnActionExecutingAsync should be called on each request, no matter what.
Feb 13, 2015 at 1:13 PM
The following is far fetched!
Is there any way i get a clone of the initial logger, so setting a private member would not affect the resolved one.
Coordinator
Feb 13, 2015 at 1:17 PM
I'm not sure I follow you.
Feb 13, 2015 at 1:26 PM
it is to far fetched.

my though was that the resolve don't return the reference of the object but a clone (serialized, deep copy). but i don't think that it does.
i will check it out in my code and try to crack this nut! i am pretty sure my code does funny this...

i let you know if i find something!
Coordinator
Feb 13, 2015 at 1:46 PM
No, Simple Injector will never clone. It just creates some service and caches it (according to its lifestyle). Also note that Simple Injector will never return an instance outside the scope of its lifestyle. So you can't resolve an instance that is created per Web API request, if the code that calls GetInstance<> is not executing within a Web API request. So the fact that you are getting an instance back, implies that are running within a Web API request, or at least within an acticve ExecutionContextScope (which is what WebApiRequestLifestyle uses under the covers).
Feb 13, 2015 at 6:36 PM
actually i got a better result by following the tutorial

http://simpleinjector.readthedocs.org/en/latest/webapiintegration.html

section

Getting the current request’s HttpRequestMessage

and injected it into the logger!

very clean very nice!!!