构造函数依赖注入的WebAPI属性 [英] Constructor Dependency Injection WebApi Attributes

查看:856
本文介绍了构造函数依赖注入的WebAPI属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在四处寻找的的WebAPI非参数注入选项属性。

我的问题很简单,这是否是真正可以使用Structuremap?

我一直在谷歌上搜索周围,但保留未来与任一属性注入(我preFER不使用)或构造注入所谓的实现,我迄今无法复制的。

我选择的容器是Structuremap不过,这样的例子就够了,因为我可以把它转换。

有谁管理呢?


解决方案

是的,这是可能的。你(像大多数人)正在行动过滤属性,这些属性方便地放入一个单独的类的微软的市场抛出,但不是所有的DI-友好。

解决的办法是打破了行动筛选器属性分为两部分作为证明这个帖子


  1. 不包含行为标志的控制器和行动的方法。
  2. 的属性
  3. 系统DI-友好的类,它实现的 IActionFilter 并包含所需的行为。

的方法是使用IActionFilter来测试属性的presence,然后执行所需的行为。动作过滤器可与所有的依赖关系(通过构造函数)提供,然后注入当应用程序组成。

  IConfigProvider提供商=新WebConfigProvider();
IActionFilter过滤器=新MaxLengthActionFilter(供应商);
config.Filters.Add(过滤器);


  

注意:如果您需要任何过滤器的依赖过一辈子不是单短,你将需要使用 GlobalFilterProvider 作为<一href=\"http://stackoverflow.com/questions/36221865/questions-about-using-ninject/36224308#36224308\">this回答。


要与StructureMap这样组装起来,你需要从你的DI配置模块返回容器的一个实例。 Application_Start方法中仍然是根组成的一部分,所以你可以在这个方法中的任何位置使用的容器,它仍然是不被视为一个服务定位器模式。请注意,我这里不显示完整的WebAPI的设置,因为我假设你已经拥有的WebAPI工作DI配置。如果你需要一个,那就是另外一回事了。

 公共类DIConfig()
{
    公共静态的IContainer寄存器()
    {
        //创建DI容器
        VAR集装箱=​​新容器();        // DI的设置配置
        container.Configure(R =&GT; r.AddRegistry&下; SomeRegistry&GT;());
        //添加其他登记这里...        #如果DEBUG
            container.AssertConfigurationIsValid();
        #万一        //我们的DI容器实例返回根组成
        返回容器中;
    }
}公共类MvcApplication:System.Web.HttpApplication
{
    保护无效的Application_Start()
    {
        //悬挂到容器实例,以便您可以解决
        //实例而仍然在组合物根
        集装箱的IContainer = DIConfig.Register();        AreaRegistration.RegisterAllAreas();        //把容器,所以我们可以解决我们的IActionFilter
        WebApiConfig.Register(GlobalConfiguration.Configuration,集装箱);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
    }
}公共静态类WebApiConfig
{
    //添加的IContainer参数
    公共静态无效的注册(HttpConfiguration配置,集装箱的IContainer)
    {
        config.Routes.MapHttpRoute(
            名称:DefaultApi
            routeTemplate:API / {}控制器/(编号),
            默认:新{ID = RouteParameter.Optional}
        );        //取消注释code以下行来启用带有一个IQueryable或IQueryable的&LT操作的查询的支持; T&GT;返回类型。
        //为了避免处理意外或恶意的查询,请使用QueryableAttribute验证设置,以验证传入查询。
        //欲了解更多信息,请访问http://go.microsoft.com/fwlink/?LinkId=279712。
        //config.EnableQuerySupport();        //加入我们的行动过滤器
        config.Filters.Add(container.GetInstance&所述; IMaxLengthActionFilter&GT;());
        //这里添加额外的过滤器,寻找其他属性...
    }
}

MaxLengthActionFilter的实施将是这个样子:

  //用于唯一标识StructureMap过滤器
公共接口IMaxLengthActionFilter:System.Web.Http.Filters.IActionFilter
{
}公共类MaxLengthActionFitler:IMaxLengthActionFilter
{
    公共只读IConfigProvider configProvider;    公共MaxLengthActionFilter(IConfigProvider configProvider)
    {
        如果(configProvider == NULL)
            抛出新的ArgumentNullException(configProvider);
        this.configProvider = configProvider;
    }    公共任务&LT; Htt的presponseMessage&GT; ExecuteActionFilterAsync(
        HttpActionContext ActionContext中,
        的CancellationToken的CancellationToken,
        FUNC&LT;任务&LT; Htt的presponseMessage&GT;&GT;续)
    {
        var属性= this.GetMaxLengthAttribute(filterContext.ActionDescriptor);
        如果(属性!= NULL)
        {
            VAR最大长度= attribute.MaxLength;
            //这里执行(前),继续你的行为,
            //并使用configProvider根据需要            。延续回报()ContinueWith(T =&GT;
            {
                //(后),继续在这里执行你的行为,
                //并使用configProvider根据需要                返回t.Result;
            });
        }
        返回继续();
    }    公共BOOL的AllowMultiple
    {
        获得{返回true; }
    }    公共MaxLengthAttribute GetMaxLengthAttribute(ActionDescriptor actionDescriptor)
    {
        MaxLengthAttribute结果= NULL;        //检查的操作方法存在的属性
        结果=(MaxLengthAttribute)actionDescriptor
            .GetCustomAttributes(typeof运算(MaxLengthAttribute),FALSE)
            .SingleOrDefault();        如果(结果!= NULL)
        {
            返回结果;
        }        //检查控制器上存在的属性
        结果=(MaxLengthAttribute)actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes(typeof运算(MaxLengthAttribute),FALSE)
            .SingleOrDefault();        返回结果;
    }
}

和,你的属性的不应该包含任何行为的应该是这个样子:

  //这个属性应该包含任何行为。没有行为,没有什么需要被注入。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,的AllowMultiple = FALSE)]
公共类MaxLengthAttribute:属性
{
    公共MaxLengthAttribute(INT最大长度)
    {
        this.MaxLength =最大长度;
    }    公众诠释MAXLENGTH {搞定;私人集; }
}

I have been looking around for a non Parameter injection option for the WebApi attributes.

My question is simply whether this is actually possible using Structuremap?

I have been googling around but keep coming up with either property injection (which I prefer not to use) or supposed implementations of constructor injection that I have thus far been unable to replicate.

My container of choice is Structuremap however any example of this will suffice as I am able to convert it.

Anyone ever managed this?

解决方案

Yes, it is possible. You (like most people) are being thrown by Microsoft's marketing of Action Filter Attributes, which are conveniently put into a single class, but not at all DI-friendly.

The solution is to break the Action Filter Attribute into 2 parts as demonstrated in this post:

  1. An attribute that contains no behavior to flag your controllers and action methods with.
  2. A DI-friendly class that implements IActionFilter and contains the desired behavior.

The approach is to use the IActionFilter to test for the presence of the attribute, and then execute the desired behavior. The action filter can be supplied with all dependencies (through the constructor) and then injected when the application is composed.

IConfigProvider provider = new WebConfigProvider();
IActionFilter filter = new MaxLengthActionFilter(provider);
config.Filters.Add(filter);

NOTE: If you need any of the filter's dependencies to have a lifetime shorter than singleton, you will need to use a GlobalFilterProvider as in this answer.

To wire this up with StructureMap, you will need to return an instance of the container from your DI configuration module. The Application_Start method is still part of the composition root, so you can use the container anywhere within this method and it is still not considered a service locator pattern. Note that I don't show a complete WebApi setup here, because I am assuming you already have a working DI configuration with WebApi. If you need one, that is another question.

public class DIConfig()
{
    public static IContainer Register()
    {
        // Create the DI container
        var container = new Container();

        // Setup configuration of DI
        container.Configure(r => r.AddRegistry<SomeRegistry>());
        // Add additional registries here...

        #if DEBUG
            container.AssertConfigurationIsValid();
        #endif

        // Return our DI container instance to the composition root
        return container;
    }
}

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Hang on to the container instance so you can resolve
        // instances while still in the composition root
        IContainer container = DIConfig.Register();

        AreaRegistration.RegisterAllAreas();

        // Pass the container so we can resolve our IActionFilter
        WebApiConfig.Register(GlobalConfiguration.Configuration, container);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
    }
}

public static class WebApiConfig
{
    // Add a parameter for IContainer
    public static void Register(HttpConfiguration config, IContainer container)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
        // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
        // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
        //config.EnableQuerySupport();

        // Add our action filter
        config.Filters.Add(container.GetInstance<IMaxLengthActionFilter>());
        // Add additional filters here that look for other attributes...
    }
}

The implementation of MaxLengthActionFilter would look something like this:

// Used to uniquely identify the filter in StructureMap
public interface IMaxLengthActionFilter : System.Web.Http.Filters.IActionFilter
{
}

public class MaxLengthActionFitler : IMaxLengthActionFilter
{
    public readonly IConfigProvider configProvider;

    public MaxLengthActionFilter(IConfigProvider configProvider)
    {
        if (configProvider == null)
            throw new ArgumentNullException("configProvider");
        this.configProvider = configProvider;
    }

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(
        HttpActionContext actionContext,
        CancellationToken cancellationToken,
        Func<Task<HttpResponseMessage>> continuation)
    {
        var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor);
        if (attribute != null)
        {
            var maxLength = attribute.MaxLength;
            // Execute your behavior here (before the continuation), 
            // and use the configProvider as needed

            return continuation().ContinueWith(t =>
            {
                // Execute your behavior here (after the continuation), 
                // and use the configProvider as needed

                return t.Result;
            });
        }
        return continuation();
    }

    public bool AllowMultiple
    {
        get { return true; }
    }

    public MaxLengthAttribute GetMaxLengthAttribute(ActionDescriptor actionDescriptor)
    {
        MaxLengthAttribute result = null;

        // Check if the attribute exists on the action method
        result = (MaxLengthAttribute)actionDescriptor
            .GetCustomAttributes(typeof(MaxLengthAttribute), false)
            .SingleOrDefault();

        if (result != null)
        {
            return result;
        }

        // Check if the attribute exists on the controller
        result = (MaxLengthAttribute)actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes(typeof(MaxLengthAttribute), false)
            .SingleOrDefault();

        return result;
    }
}

And, your attribute which should not contain any behavior should look something like this:

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

    public int MaxLength { get; private set; }
}

这篇关于构造函数依赖注入的WebAPI属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆