asp.net-web-api dependency-injection structuremap custom-attributes structuremap3

asp.net web api - Constructor Dependencia Inyección WebApi Atributos



asp.net-web-api dependency-injection (2)

He estado buscando una opción de inyección sin parámetros para los atributos de WebApi.

Mi pregunta es simplemente si esto es realmente posible usando Structuremap?

He estado buscando en Google, pero sigo apareciendo con la inyección de propiedades (que prefiero no usar) o con supuestas implementaciones de inyección de constructores que hasta ahora no he podido replicar.

Mi contenedor de elección es Structuremap, sin embargo, cualquier ejemplo de esto será suficiente ya que puedo convertirlo.

¿Alguien ha logrado esto?


Luché con proveedores de filtros de acción personalizados, sin que funcionara para mis atributos de autenticación. También probé varios enfoques con el constructor y la inyección de propiedades, pero no encontré una solución que se sintiera bien.

Finalmente terminé inyectando funciones en mis atributos. De esa manera, las pruebas unitarias pueden inyectar una función que devuelve un falso o simulacro, mientras que la aplicación puede inyectar una función que resuelve la dependencia con el contenedor IoC.

Acabo de escribir sobre este enfoque aquí: http://danielsaidi.com/blog/2015/09/11/asp-net-and-webapi-attributes-with-structuremap

Funciona muy bien en mi proyecto y resuelve todos los problemas que tuve con los otros enfoques.


Sí, es posible. Usted (como la mayoría de las personas) está siendo arrojado por el marketing de Microsoft de los atributos de filtro de acción, que se colocan convenientemente en una sola clase, pero no son aptos para DI.

La solución es dividir el atributo de filtro de acción en 2 partes, como se demuestra en esta publicación :

  1. Un atributo que no contiene ningún comportamiento para marcar sus controladores y métodos de acción.
  2. Una clase IActionFilter DI que implementa IActionFilter y contiene el comportamiento deseado.

El enfoque es utilizar el IActionFilter para probar la presencia del atributo y luego ejecutar el comportamiento deseado. El filtro de acción se puede suministrar con todas las dependencias (a través del constructor) y luego se inyecta cuando la aplicación está compuesta.

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

NOTA: Si necesita que cualquiera de las dependencias del filtro tenga una vida útil más corta que singleton, deberá usar un GlobalFilterProvider como en esta respuesta .

Para conectar esto con StructureMap, deberá devolver una instancia del contenedor desde su módulo de configuración DI. El método Application_Start sigue siendo parte de la raíz de la composición, por lo que puede usar el contenedor en cualquier lugar dentro de este método y aún no se considera un patrón de localización de servicios. Tenga en cuenta que no muestro una configuración completa de WebApi aquí, porque supongo que ya tiene una configuración DI activa con WebApi. Si necesita uno, esa es otra pregunta.

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... } }

La implementación de MaxLengthActionFilter se vería así:

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

Y, su atributo que no debe contener ningún comportamiento debería verse así:

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