patron inyeccion ejemplos diseño dependencias anexsoft c# .net asp.net-mvc dependency-injection

ejemplos - patron de diseño inyeccion de dependencias c#



¿Cómo usar la inyección de dependencia con un atributo? (2)

¿Cuál es la mejor manera de inyectar algo en una clase de atributo?

Estrictamente hablando, no podemos usar la inyección de dependencia para inyectar una dependencia en un atributo. Los atributos son para metadatos, no comportamiento. [AttributeSpecification()] fomenta esto al prohibir los tipos de referencia como argumentos.

Lo que probablemente esté buscando es usar un atributo y un filtro juntos, y luego inyectar dependencias en el filtro . El atributo agrega metadatos, que determina si aplicar el filtro, y el filtro recibe las dependencias inyectadas.

¿Cómo usar la inyección de dependencia con un atributo?

Hay muy pocas razones para hacer esto.

Dicho esto, si IApplicationModelProvider inyectar en un atributo, puede usar ASP.NET Core MVC IApplicationModelProvider . El marco pasa dependencias al constructor del proveedor y el proveedor puede pasar dependencias a las propiedades o métodos del atributo.

En su Inicio, registre su proveedor.

using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; public class Startup { public void ConfigureServices(IServiceCollection services) { services.TryAddEnumerable(ServiceDescriptor.Transient <IApplicationModelProvider, MyApplicationModelProvider>()); services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } }

Use la inyección de constructor en el proveedor y pase esas dependencias al atributo.

using System.Linq; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Routing; public class MyApplicationModelProvider : IApplicationModelProvider { private IUrlHelperFactory _urlHelperFactory; // constructor injection public MyApplicationModelProvider(IUrlHelperFactory urlHelperFactory) { _urlHelperFactory = urlHelperFactory; } public int Order { get { return -1000 + 10; } } public void OnProvidersExecuted(ApplicationModelProviderContext context) { foreach (var controllerModel in context.Result.Controllers) { // pass the depencency to controller attibutes controllerModel.Attributes .OfType<MyAttribute>().ToList() .ForEach(a => a.UrlHelperFactory = _urlHelperFactory); // pass the dependency to action attributes controllerModel.Actions.SelectMany(a => a.Attributes) .OfType<MyAttribute>().ToList() .ForEach(a => a.UrlHelperFactory = _urlHelperFactory); } } public void OnProvidersExecuting(ApplicationModelProviderContext context) { // intentionally empty } }

Crea un atributo con setters públicos que pueden recibir dependencias.

using System; using Microsoft.AspNetCore.Mvc.Routing; public sealed class MyAttribute : Attribute { private string _someParameter; public IUrlHelperFactory UrlHelperFactory { get; set; } public MyAttribute(string someParameter) { _someParameter = someParameter; } }

Aplicar el atributo a un controlador o una acción.

using Microsoft.AspNetCore.Mvc; [Route("api/[controller]")] [MyAttribute("SomeArgument")] public class ValuesController : Controller { [HttpGet] [MyAttribute("AnotherArgument")] public string Get() { return "Foobar"; } }

Lo anterior demuestra de una manera, para el caso de uso raro, que puede inyectar dependencias en un atributo. Si encuentra una razón válida para hacerlo, publíquelo en los comentarios.

En un proyecto de MVC que estoy creando, tengo el siguiente RequirePermissionAttribute que se pone en cualquier acción que necesita permisos específicos (se ha simplificado para este ejemplo):

public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter { public Operation Permissions { get; set; } public RequirePermissionAttribute() { } public RequirePermissionAttribute(Operation permissions) { this.Permissions = permissions; } public bool AuthorizeCore(HttpContextBase httpContext) { IAuthorizationService authServ = new ASPNETAuthorizationService(); return authServ.Authorize(httpContext); } public void OnAuthorization(AuthorizationContext filterContext) { Enforce.ArgNotNull(filterContext); if (this.AuthorizeCore(filterContext.HttpContext)) { // code snipped. } else { // code snipped. } } }

Así que el problema obviamente con esto es que mi atributo authorize tiene una dependencia en ASPNETAuthorizationService que he creado. No puedo seguir el camino del constructor ya que los atributos se comprueban en tiempo de compilación.

Una cosa para mencionar, estoy usando mi propio IoC pequeño que hice y no tiene soporte para inyección de propiedad (todavía). Por supuesto, si fuera la ruta de inyección de la propiedad, tendría que agregar soporte para eso (sobre lo cual tendría que investigar un poco).

¿Cuál es la mejor manera de inyectar algo en una clase de atributo?


Originalmente pensé que esto no era posible, pero estoy corregido. Aquí hay un ejemplo con Ninject:

http://codeclimber.net.nz/archive/2009/02/10/how-to-use-ninject-to-inject-dependencies-into-asp.net-mvc.aspx

Actualización 2016-10-13

Esta es una pregunta bastante antigua por el momento, y los marcos han cambiado bastante. Ninject ahora le permite agregar enlaces a filtros específicos basados ​​en la presencia de atributos específicos, con un código como este:

// LogFilter is applied to controllers that have the LogAttribute this.BindFilter<LogFilter>(FilterScope.Controller, 0) .WhenControllerHas<LogAttribute>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to actions that have the LogAttribute this.BindFilter<LogFilter>(FilterScope.Action, 0) .WhenActionHas<LogAttribute>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to all actions of the HomeController this.BindFilter<LogFilter>(FilterScope.Action, 0) .WhenControllerTypeIs<HomeController>() .WithConstructorArgument("logLevel", Level.Info); // LogFilter is applied to all Index actions this.BindFilter(FilterScope.Action, 0) .When((controllerContext, actionDescriptor) => actionDescriptor.ActionName == "Index") .WithConstructorArgument("logLevel", Level.Info);

Esto está en consonancia con el principio, argumentado por Mark Seeman y por el autor de Simple Injector , que es que debe mantener la lógica de su filtro de acción separada de la clase de atributo personalizado.

MVC 5 y 6 también hacen que sea mucho más fácil inyectar valores en atributos de lo que solía ser. Aún así, separar el filtro de acción de su atributo es realmente el mejor enfoque a seguir.