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:
- An
Attribute
subclass (that does not contain any behavior).
- 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:
- Inject an Abstract Factory that creates your
MyClass
instance instead of injecting the class directly into the filter.
- 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.