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 :
- Un atributo que no contiene ningún comportamiento para marcar sus controladores y métodos de acción.
- 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; }
}