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

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

问题描述

我一直在寻找 WebApi 属性的非参数注入选项.

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

我一直在谷歌上搜索,但一直在想出属性注入(我不喜欢使用)或构造函数注入的假设实现,我迄今为止无法复制.

我选择的容器是 Structuremap,但是我可以转换它的任何示例就足够了.

有人管理过这个吗?

解决方案

是的,这是可能的.您(和大多数人一样)被 Microsoft 的 Action Filter Attributes 营销所吸引,这些属性可以方便地放入单个类中,但根本不适合 DI.

解决方案是将 Action Filter Attribute 分成 2 部分 如所示这篇文章:

  1. 不包含用于标记控制器和操作方法的行为的属性.
  2. 实现 IActionFilter 并包含所需的行为.

方法是使用 IActionFilter 来测试属性是否存在,然后执行所需的行为.操作过滤器可以提供所有依赖项(通过构造函数),然后在应用程序组合时注入.

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

<块引用>

注意:如果您需要过滤器的任何依赖项的生命周期比单例短,则需要使用 GlobalFilterProvider,如这个答案.

要将其与 StructureMap 连接起来,您需要从 DI 配置模块返回容器的实例.Application_Start 方法仍然是组合根的一部分,所以你可以在这个方法中的任何地方使用容器,它仍然不被视为服务定位器模式.请注意,我没有在此处展示完整的 WebApi 设置,因为我假设您已经有了 WebApi 的有效 DI 配置.如果你需要,那是另一个问题.

公共类DIConfig(){公共静态 IContainer 注册(){//创建DI容器var 容器 = 新容器();//设置DI的配置container.Configure(r => r.AddRegistry());//在此处添加其他注册表...#if 调试container.AssertConfigurationIsValid();#万一//将我们的 DI 容器实例返回到组合根返回容器;}}公共类 MvcApplication : System.Web.HttpApplication{protected void Application_Start(){//挂在容器实例上,这样你就可以解决//仍然在组合根中的实例IContainer 容器 = DIConfig.Register();AreaRegistration.RegisterAllAreas();//传递容器,以便我们可以解析我们的 IActionFilterWebApiConfig.Register(GlobalConfiguration.Configuration, 容器);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);AuthConfig.RegisterAuth();}}公共静态类 WebApiConfig{//为 IContainer 添加一个参数公共静态无效注册(HttpConfiguration 配置,IContainer 容器){config.Routes.MapHttpRoute(name: "DefaultApi",routeTemplate: "api/{controller}/{id}",默认值:新 { id = RouteParameter.Optional });//取消对以下代码行的注释以启用对具有 IQueryable 或 IQueryable 的操作的查询支持返回类型.//为避免处理意外或恶意查询,请使用 QueryableAttribute 上的验证设置来验证传入查询.//有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=279712.//config.EnableQuerySupport();//添加我们的动作过滤器config.Filters.Add(container.GetInstance());//在此处添加其他过滤器以查找其他属性...}}

MaxLengthActionFilter 的实现看起来像这样:

//用于在 StructureMap 中唯一标识过滤器公共接口 IMaxLengthActionFilter : System.Web.Http.Filters.IActionFilter{}公共类 MaxLengthActionFitler : IMaxLengthActionFilter{公共只读 IConfigProvider configProvider;公共 MaxLengthActionFilter(IConfigProvider configProvider){if (configProvider == null)throw new ArgumentNullException("configProvider");this.configProvider = configProvider;}公共任务ExecuteActionFilterAsync(HttpActionContext actionContext,CancellationToken 取消令牌,Func<Task<HttpResponseMessage>>续){var 属性 = this.GetMaxLengthAttribute(filterContext.ActionDescriptor);如果(属性!= null){var maxLength = attribute.MaxLength;//在这里执行你的行为(在继续之前),//并根据需要使用 configProviderreturn continuation().ContinueWith(t =>{//在这里执行你的行为(在延续之后),//并根据需要使用 configProvider返回 t.Result;});}返回继续();}public bool AllowMultiple{得到 { 返回真;}}公共 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 : 属性{公共最大长度属性(int maxLength){this.MaxLength = maxLength;}公共 int 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天全站免登陆