2011-11-20 29 views
8

Al momento, ho un ControllerFactory personalizzato in cui mi inietto il mio contenitore Unità:con Unity per iniettare le dipendenze in un personalizzato ActionFilter

a global.asax Application_Start():

var container = InitContainer(); 
DependencyResolver.SetResolver(new UnityDependencyResolver(container)); 

var factory = new UnityControllerFactory(container); 
ControllerBuilder.Current.SetControllerFactory(factory); 

Nel controller fabbrica ho impostato il mio controller per utilizzare un ActionInvoker personalizzato in questo modo:

protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) 
{ 
    var controller = base.GetControllerInstance(requestContext, controllerType) as Controller; 

    if (controller != null) 
     controller.ActionInvoker = new UnityActionInvoker(_container); 

    return controller; 
} 

Infine nel mio ActionInvoker personalizzato, tento di azioni accumulo essere invocati usando il contenitore ActionInvokers:

protected override ActionExecutedContext InvokeActionMethodWithFilters(
     ControllerContext controllerContext, 
     IList<IActionFilter> filters, 
     ActionDescriptor actionDescriptor, 
     IDictionary<string, object> parameters) 
{ 
    var builtUpFilters = new List<IActionFilter>(); 

    foreach (IActionFilter actionFilter in filters) 
    { 
     builtUpFilters.Add(_container.BuildUp<IActionFilter>(actionFilter)); 
    } 

    return base.InvokeActionMethodWithFilters(controllerContext, builtUpFilters, actionDescriptor, parameters); 
} 

Ecco un esempio di una delle ActionFilters che si sta costruendo in su:

public class PopulatRolesAttribute : ActionFilterAttribute, IActionFilter 
{ 
    private const string RolesKey = "roles"; 

    [Dependency] 
    public Func<IMetadataService> Service { get; set; } 

    public PopulatRolesAttribute() 
    { 
    } 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (filterContext.Controller.ViewData[RolesKey] == null) 
     { 
      filterContext.Controller.ViewData[RolesKey] = Service().GetRoles(); 
     } 
    } 
} 

Il problema è che la proprietà pubblica sulla mia abitudine ActionFilterAttribute non è mai iniettato con qualsiasi cosa, rimane nullo in esecuzione! Non riesco a capire perché il mio filtro non sia stato correttamente creato dal contenitore. Il tipo di essere iniettato è registrato correttamente, in questo modo:

container.RegisterInstance(new ChannelFactory<IMetadataService>(
    new BasicHttpBinding(), 
    new EndpointAddress("http://example.com/ABSApplication/MetadataService.svc"))); 

container.RegisterInstance<Func<IMetadataService>>(
    () => container.Resolve<ChannelFactory<IMetadataService>>().CreateChannel()); 

E viene anche iniettato in altre parti dell'applicazione (Anche se non tramite .Buildup). Questo è praticamente lo stesso processo seguito da questo blog post. Quale parte del puzzle mi manca?

+0

Per curiosità, perché non farlo in diciamo, autenticare richiesta, richiesta preauth, ecc e li memorizza nella cache (sessione, ecc) in modo che non ci sono per ogni richiesta mentre l'utente è connesso? –

+0

La mia intenzione originale era di incapsulare questa funzionalità in modo che potessi aggiungerla ogni volta che un'azione lo richiedeva. Se dopo la revisione stavo per utilizzare i dati recuperati dai filtri di una quantità significativa, sicuramente lo memorizzerei in una cache globale per riutilizzarli in tutta l'applicazione. In realtà sta iniziando a sembrare che sarebbe comunque una soluzione più semplice, indipendentemente dal riutilizzo! Sono ancora molto curioso del perché 'container.BuildUp' non funzioni comunque. –

+0

Ho trovato questo esempio, andando a controllare a breve. La fonte è disponibile per il download anche qui http://msdn.microsoft.com/en-us/gg618494 –

risposta

8

Lo farei in modo leggermente diverso. Lo farei:

  1. installare il pacchetto NuGet unity.mvc3

  2. chiamata bootstrapped.initialise(), come indicato nel documento txt il pacchetto aggiunge al progetto

  3. definire la mappatura IMetadataService nella inizializzazione per il vostro tipo di calcestruzzo

  4. add IMetadataService al costruttore

La differenza tra l'implementazione e l'articolo di riferimento è l'utilizzo di Func, che non sono sicuro se questo aggiunge un problema diverso al mix qui. Devo immaginare che faccia come il metodo di cui sopra (senza Func) funziona bene per me.

Edit: il codice di Brad Wilson ha lavorato bene per me qui: http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html

Parti applicabili (si veda il link qui sopra)

Global.asax.cs

 

protected void Application_Start() { 
    // ... 

    var oldProvider = FilterProviders.Providers.Single(
     f => f is FilterAttributeFilterProvider 
    ); 
    FilterProviders.Providers.Remove(oldProvider); 

    var container = new UnityContainer(); 
    var provider = new UnityFilterAttributeFilterProvider(container); 
    FilterProviders.Providers.Add(provider); 

    // ... 
} 
 

Il filtro stesso:

 

using System; 
using System.Web.Mvc; 
using Microsoft.Practices.Unity; 

public class InjectedFilterAttribute : ActionFilterAttribute { 

    [Dependency] 
    public IMathService MathService { get; set; } 

    public override void OnResultExecuted(ResultExecutedContext filterContext) { 
     filterContext.HttpContext.Response.Write(
      String.Format("

The filter says 2 + 3 is {0}.

", MathService.Add(2, 3)) ); } }

e UnityFilterAttributeFilterProvider.cs

 

using System.Collections.Generic; 
using System.Web.Mvc; 
using Microsoft.Practices.Unity; 

public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider { 
    private IUnityContainer _container; 

    public UnityFilterAttributeFilterProvider(IUnityContainer container) { 
     _container = container; 
    } 

    protected override IEnumerable GetControllerAttributes(
       ControllerContext controllerContext, 
       ActionDescriptor actionDescriptor) { 

     var attributes = base.GetControllerAttributes(controllerContext, 
                 actionDescriptor); 
     foreach (var attribute in attributes) { 
      _container.BuildUp(attribute.GetType(), attribute); 
     } 

     return attributes; 
    } 

    protected override IEnumerable GetActionAttributes(
       ControllerContext controllerContext, 
       ActionDescriptor actionDescriptor) { 

     var attributes = base.GetActionAttributes(controllerContext, 
                actionDescriptor); 
     foreach (var attribute in attributes) { 
      _container.BuildUp(attribute.GetType(), attribute); 
     } 

     return attributes; 
    } 
} 
 
+1

Apprezzo la direzione del pacchetto Unity.Mvc3 - incapsula la risoluzione delle dipendenze per Mvc3 usando Unity in modo piacevole. Comunque non penso che risolva il mio problema. Se ho un metodo in un controller decorato con un filtro come questo: '[PopulatRoles] public ActionResult Add() {' ​​Come faccio a ottenere l'iniezione su PopulateRoles ActionFilterAttribute? Non posso usare l'iniezione del costruttore poiché è un attributo e non posso utilizzare il suo costruttore. Pertanto ho bisogno di costruire l'attributo così come è stato creato. –

+0

Come appendice, ho visto che ninject fa qualcosa di completamente diverso come questo [link] (http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for- mvc3 /) per consentire l'iniezione del costruttore sui filtri, tuttavia non sono sicuro che ciò sia possibile con Unity. –

+0

I filtri sono risolti internamente in MVC Dovrò provare un test rapido qui. –

Problemi correlati