1

I have a small filter

public class Action1DebugActionWebApiFilter : ActionFilterAttribute
{
    public IMyclass myClass { get; set; }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // pre-processing
        Debug.WriteLine("ACTION 1 DEBUG pre-processing logging");
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var objectContent = actionExecutedContext.Response.Content as ObjectContent;
        if (objectContent != null)
        {
            var type = objectContent.ObjectType; //type of the returned object
            var value = objectContent.Value; //holding the returned value
        }

        Debug.WriteLine("ACTION 1 DEBUG  OnActionExecuted Response " + actionExecutedContext.Response.StatusCode.ToString());
    }
}

I would like for IMyclass to have injected a class that gets resolved from AutoFac.

builder.RegisterType<IMyclass >().As<MyClass>().InstancePerRequest();

however it is still null. All my other injections are working fine in the constructor but not in the property.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
TotalWar
  • 335
  • 1
  • 6
  • 16
  • _All my other injections are working fine in the constructor but not in the property_ : I'm confused: your attribute has a parameterless constructor. – Fabio Salvalai Oct 02 '15 at 13:03
  • not used autofac but see here http://stackoverflow.com/questions/17906329/autofac-attribute-injection-failing-on-attributes does `PropertiesAutowired` have anything to do with it? ie registering your attribute `builder.RegisterType().PropertiesAutowired()` – Ric Oct 02 '15 at 13:07
  • @FabioSalvalai you can use property injection too! – Ric Oct 02 '15 at 13:10
  • 1
    of course, Ric. The thing is that this attribute has no .ctor, therefore no .ctor injections. @TotalWar claims the other injections work fine in the .ctor, and therefore assumes the IoC does his job. but since there is no injection in the .ctor, you can't draw that conclusion. My guess is: the attribute is not resolved by the IoC container, and therefore, no injections, property or otherwise, are performed. – Fabio Salvalai Oct 02 '15 at 13:12
  • Agreed, see my anser. – Ric Oct 02 '15 at 13:13

3 Answers3

3

Attributes are not DI-friendly at all. They are instantiated by the .NET framework, so you have no control over what dependencies they have. So, the best solution is to abandon the thought of using ActionFilterAttribute altogether.

As pointed out in passive attributes, you can break down your ActionFilterAttribute into its 2 inherited features:

  1. An Attribute subclass (that does not contain any behavior).
  2. A DI-friendly IActionFilter subclass (that uses constructor injection).

Action1DebugAttribute

First, there is the attribute to mark your controllers and actions with. This attribute contains no behavior at all (but it may contain properties like in this example if desired).

// This attribute should contain no behavior. No behavior, nothing needs to be injected.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class Action1DebugAttribute : Attribute
{}

Action1DebugActionWebApiFilter

This is the DI-friendly action filter. We can use constructor injection or property injection if desired. This example uses constructor injection for the sake of simplicity.

public class Action1DebugActionWebApiFilter : IActionFilter
{
    private readonly IMyclass myClass;

    public Action1DebugActionWebApiFilter(IMyClass myClass)
    {
        if (myClass == null)
            throw new ArgumentNullException("myClass");
        this.myClass = myClass;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (this.IsFilterDefined(actionContext.ActionDescriptor))
        {
            // pre-processing
            Debug.WriteLine("ACTION 1 DEBUG pre-processing logging");
        }
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (this.IsFilterDefined(actionExecutedContext.ActionDescriptor))
        {
            var objectContent = actionExecutedContext.Response.Content as ObjectContent;
            if (objectContent != null)
            {
                var type = objectContent.ObjectType; //type of the returned object
                var value = objectContent.Value; //holding the returned value
            }

            Debug.WriteLine("ACTION 1 DEBUG  OnActionExecuted Response " + actionExecutedContext.Response.StatusCode.ToString());
        }
    }

    private bool IsFilterDefined(ActionDescriptor actionDescriptor)
    {
        return actionDescriptor.IsDefined(typeof(Action1DebugAttribute), inherit: true)
            || actionDescriptor.ControllerDescriptor.IsDefined(typeof(Action1DebugAttribute), inherit: true);
    }
}

Usage

Once you do this, you can simply use Autofac to resolve your action filter and all of its dependencies in your composition root.

Registration

builder.RegisterType<IMyclass>().As<MyClass>();
// Since it is possible more than one `IActionFilter` is registered,
// we are using a named type. You could alternatively create another 
// interface to uniquely identify this action filter.
builder.RegisterType<IActionFilter>()
       .Named<Action1DebugActionWebApiFilter>("action1DebugActionWebApiFilter");

Resolving

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters, IContainer container)
    {
        filters.Add(container.ResolveNamed<IActionFilter>("action1DebugActionWebApiFilter"));
        filters.Add(new HandleErrorAttribute());
    }
}

Lifetime

You won't be able to use instance per request lifetime because action filters are created as part of MVC's object graph, not resolved per request.

However, your desire to do so indicates that the IMyClass instance has some state in it that applies to the current request. If that is not the case, then you can use this code as is.

On the other hand, if your object is stateful, then you can use one of the following approaches to resolve it at runtime:

  1. Inject an Abstract Factory that creates your MyClass instance instead of injecting the class directly into the filter.
  2. Inject a Func<Type, IMyClass> into the filter in order to call an anonymous method inside of your composition root that uses the container to resolve the instance as shown here.
Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • 'You won't be able to use instance per request lifetime because action filters are created as part of MVC's object graph, not resolved per request.' I was actually planning to do a database call to get a user's roles from his id ( that i get from his claim). – TotalWar Oct 02 '15 at 22:52
  • In that case, just use an abstract factory to instantiate your dbcontext into a using block so it is disposed of properly. – NightOwl888 Oct 04 '15 at 00:46
  • 'You won't be able to use instance per request lifetime because action filters are created as part of MVC's object graph, not resolved per request.' Are you sure this still holds? The autofac doucmentation still says so, but i'm using an IAutofacActionFilter on an application i'm developing, and i'm getting different instances for each request. I've tested by adding a GUID property to my dependency that's initialized on the dependency constructor. And each request get's the dependency with a new GUID. Using WebApi v2.2 and Autofac v3.5.2 and "Autofac WebApi 2.2 Integration" v3.4.0 – andyroschy Feb 25 '16 at 15:36
  • @andyroschy - You are correct. Not sure what I was thinking when I posted this. I am removing the part about lifetime. – NightOwl888 Feb 25 '16 at 15:42
  • @NightOwl888 Well, according to the official Autofac documentation you were correct http://docs.autofac.org/en/latest/faq/per-request-scope.html#no-per-request-filter-dependencies-in-web-api , there's even a GitHub issue where it says so https://github.com/autofac/Autofac/issues/525 So i'm not sure if it's something that changed on Autofac but the documentation wasn't updated, or something that change on the WebApi framework. Or if it's something that works while Debuggin but not on when deployed, which worries me a bit. – andyroschy Feb 25 '16 at 16:32
  • 1
    Hmm...Well, WebApi and MVC are completely different frameworks, each with their own separate configuration. I suppose the original answer was correct because the action filters in each framework are similar - I rolled it back. You might want to check the source to see how an IAutofacActionFilter is handled. – NightOwl888 Feb 25 '16 at 17:16
  • @NightOwl888 Oh, my mistake i didn't get that this was and MVC answer and not WebApi. The documentation link i pasted refers to WebApi though ,so i'm still confused, i'll have to dig further on this. Thanks! – andyroschy Feb 25 '16 at 17:55
  • My bad, this whole question (and answer) is about WebApi, not MVC. I looked at the tags, and it is incorrectly tagged `asp.net-mvc` (and not webapi), so I made an incorrect assumption. But the advice about `IAutofacActionFilter` still applies. – NightOwl888 Feb 25 '16 at 18:34
1

Did you follow the steps described in the AutoFac documentation in order to use the IoC for attributes ?

Enable Property Injection for Action Filters

To make use of property injection for your filter attributes call the RegisterFilterProvider() method on the ContainerBuilder before building your container and providing it to the AutofacDependencyResolver.

builder.RegisterFilterProvider();
Community
  • 1
  • 1
Fabio Salvalai
  • 2,479
  • 17
  • 30
0

From reading the documentation and seeing other examples on this site eg:

autofac-attribute-injection and how to use property injection with autofac, it seems that what you need is:

builder.RegisterType<IMyclass>().As<MyClass>().InstancePerRequest();
builder.RegisterType<Action1DebugActionWebApiFilter>().PropertiesAutowired();
Community
  • 1
  • 1
Ric
  • 12,855
  • 3
  • 30
  • 36
  • seems that from reading the documentation, it is not that straight forward: http://docs.autofac.org/en/stable/integration/webapi.html#provide-filters-via-dependency-injection – Ric Oct 02 '15 at 13:56