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

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

问题描述

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



我的问题只是这是否可以使用Structuremap?



我一直在搜索,但不断提出属性注入(我不喜欢使用)或假设实现的构造函数注入到目前为止我无法复制。 p>

我的容器选择是Structuremap,但是任何一个例子都足够了,因为我可以转换它。



任何人曾经管理过这个?

解决方案

是的,这是可能的。您(像大多数人一样)被微软的行动过滤器属性推销,这些属性可以方便地放在一个单一的类中,但并不适用于DI-friendly。



解决方案是将Action Filter Attribute分为两部分如本文所示


  1. 一个属性,不包含标记您的控制器和操作方法的行为。

  2. 一个实现

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

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




注意如果您需要任何过滤器的依赖项的使用寿命比单例更短,您将需要使用 GlobalFilterProvider ,如这个答案


要连接到StructureMap,您将需要从DI配置模块返回容器的实例。 Application_Start方法仍然是组合根的一部分,因此您可以在此方法中的任何位置使用该容器,并且它仍不被视为服务定位器模式。请注意,我没有在这里显示一个完整的WebApi设置,因为我假设您已经有一个工作DI配置WebApi。如果你需要一个,那就是另一个问题。

  public class DIConfig()
{
public static IContainer Register()
{
//创建DI容器
var container = new Container();

//设置配置DI
container.Configure(r => r.AddRegistry< SomeRegistry>());
//在这里添加额外的注册表

#if DEBUG
container.AssertConfigurationIsValid();
#endif

//将我们的DI容器实例返回到组合根
return container;
}
}

public class MvcApplication:System.Web.HttpApplication
{
protected void Application_Start()
{
//挂载到容器实例,以便您可以在组合根中找到
//实例
IContainer container = DIConfig.Register();

AreaRegistration.RegisterAllAreas();

//传递容器,以便我们可以解决我们的IActionFilter
WebApiConfig.Register(GlobalConfiguration.Configuration,container);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}

public static class WebApiConfig
{
//为IContainer添加一个参数
public static void Register(HttpConfiguration config, IContainer容器)
{
config.Routes.MapHttpRoute(
name:DefaultApi,
routeTemplate:api / {controller} / {id},
默认值:new {id = RouteParameter.Optional}
);

//取消注释以下代码行以启用对IQueryable或IQueryable< T>的操作的查询支持。返回类型。
//为了避免处理意外或恶意查询,请使用QueryableAttribute上的验证设置来验证传入的查询。
//有关详细信息,请访问http://go.microsoft.com/fwlink/?LinkId=279712。
//config.EnableQuerySupport();

//添加我们的动作过滤器
config.Filters.Add(container.GetInstance< IMaxLengthActionFilter>());
//在此处添加其他属性的其他过滤器...
}
}

MaxLengthActionFilter的实现将如下所示:

  //用于唯一标识过滤器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 cancelToken,
Func< Task< HttpResponseMessage>>续)
{
var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor) ;
if(attribute!= null)
{
var maxLength = attribute.MaxLength;
//在这里执行你的行为(继续之前),
//并根据需要使用configProvider

return continuation()。ContinueWith(t =>
{
//在这里执行你的行为(继续之后),
//并根据需要使用configProvider

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

public bool AllowMultiple
{
get {return true;
}

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

//检查action方法是否存在
result =(MaxLengthAttribute)actionDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute),false)
.SingleOrDefault( );

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

//检查控件上是否存在该属性
result =(MaxLengthAttribute)actionDescriptor
.ControllerDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute) ,false)
.SingleOrDefault();

返回结果;
}
}

而且,您的属性不应包含任何行为应该如下所示:

  //此属性不应包含任何行为。没有行为,没有什么需要注射。 
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,AllowMultiple = false)]
public class MaxLengthAttribute:Attribute
{
public MaxLengthAttribute(int maxLength)
{
this.MaxLength = maxLength;
}

public int MaxLength {get;私人集合}
}


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天全站免登陆