将属性注入Web api过滤器Autofac

时间:2015-10-02 13:00:25

标签: c# asp.net asp.net-mvc dependency-injection autofac

我有一个小过滤器

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());
    }
}

我希望IMyclass注入一个从AutoFac解析的类。

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

然而它仍然是null。我所有的其他注射在构造函数中都能正常工作,但在属性中却没有。

3 个答案:

答案 0 :(得分:3)

属性根本不是DI友好的。它们由.NET框架实例化,因此您无法控制它们具有哪些依赖项。因此,最好的解决方案是放弃完全使用ActionFilterAttribute的想法。

正如passive attributes所指出的那样,您可以将ActionFilterAttribute细分为2个继承的功能:

  1. Attribute子类(不包含任何行为)。
  2. DI友好的IActionFilter子类(使用构造函数注入)。
  3. Action1DebugAttribute

    首先,有一个属性用于标记您的控制器和操作。此属性根本不包含任何行为(但如果需要,它可能包含this example中的属性)。

    // 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

    这是DI友好的动作过滤器。如果需要,我们可以使用构造函数注入或属性注入。为简单起见,此示例使用构造函数注入。

    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);
        }
    }
    

    用法

    执行此操作后,您只需使用Autofac即可解决动作过滤器及其合成根目录中的所有依赖项。

    注册

    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");
    

    解决

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

    寿命

    您将无法使用每个请求生命周期的实例,因为动作过滤器是作为MVC对象图的一部分创建的,而不是按请求解析。

    但是,您希望这样做表示IMyClass实例中有一些适用于当前请求的状态。如果不是这样,那么您可以按原样使用此代码。

    另一方面,如果您的对象是有状态的,那么您可以使用以下方法之一在运行时解决它:

    1. 注入创建MyClass实例的Abstract Factory,而不是直接将类注入过滤器。
    2. Func<Type, IMyClass>注入过滤器,以便在使用容器解析实例as shown here的合成根内部调用匿名方法。

答案 1 :(得分:0)

阅读文档并查看本网站上的其他示例,例如:

autofac-attribute-injectionhow to use property injection with autofac,您似乎需要的是:

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

答案 2 :(得分:0)

您是否按照AutoFac documentation中描述的步骤使用IoC进行属性?

  

为操作过滤器启用属性注入

     

要为您的过滤器属性使用属性注入,请在构建容器并将其提供给RegisterFilterProvider()之前调用ContainerBuilder上的AutofacDependencyResolver方法。

builder.RegisterFilterProvider();