2011-01-16 1 views
4

Je suis en train de mettre à niveau une application web de ASP.NET 3 Preview 1 vers RTM et je suis confus par l'approche mise à jour de l'injection de dépendances. J'utilise StructureMap pour cela mais ce n'est pas vraiment pertinent pour ma question. Auparavant, tout ce que je devais faire était comme suit:Comment configurer l'injection de dépendances avec ASP.NET MVC 3 RTM

x.For<IControllerFactory>().Use<DefaultControllerFactory>(); 
x.For<IServiceLocator>().Use(MvcServiceLocator.Current); 

Maintenant, il semble que je dois fournir des implémentations de IControllerActivator, IViewPageActivator et ModelMetadataProvider parce que sinon je reçois une erreur de StructureMap parce que MVC tente de les localiser en utilisant le résolveur de dépendance . D'un coup d'œil à la source MVC, il ne semble pas y avoir d'implémentations par défaut publiques. Ai-je manqué quelque chose en les mettant en place? Sûrement ceux-ci devraient être configurés par convention? Des exemples de ce qui a besoin de configuration et comment avec StructureMap seraient appréciés.

Pour référence, je suis actuellement en utilisant la bidouille laide suivante qui force MVC à utiliser ses défauts internes:

x.For<IControllerFactory>().Use<DefaultControllerFactory>(); 
x.For<IDependencyResolver>().Use(() => DependencyResolver.Current);     
x.For<IControllerActivator>().Use(() => null); 
x.For<IViewPageActivator>().Use(() => null); 
x.For<ModelMetadataProvider>().Use(ModelMetadataProviders.Current); 

EDIT: Pour être clair, j'ai une implémentation StructureMap travail de la dépendance résolveur - la question est pourquoi MVC se plaint à propos de toutes ces interfaces ne sont pas configurés dans le conteneur.

Répondre

4

J'ai compris cela grâce au lien affiché dans Carman @ Michael un commentaire sur sa réponse. Je ne suis pas sûr de l'étiquette ici pour savoir si cela justifie d'accepter sa réponse réelle car ce n'était pas tout à fait exact (je lui ai donné +1 vote) mais je pensais poster ma propre réponse pour expliquer exactement ce que le question était.

Le problème était dû à une combinaison de mon implémentation de IDependencyResolver et de ma configuration de conteneur. Au départ, je devais:

public class StructureMapDependencyResolver : IDependencyResolver 
{ 
    public object GetService(Type serviceType) 
    { 
     return ObjectFactory.GetInstance(serviceType); 
    } 

    public IEnumerable<object> GetServices(Type serviceType) 
    { 
     foreach (object obj in ObjectFactory.GetAllInstances(serviceType)) 
     { 
      yield return obj; 
     } 
    } 
} 

mais je l'ai changé à cette fonction de Steve Smith's blog post liée à l'Jeremy Miller's blog post:

public class StructureMapDependencyResolver : IDependencyResolver 
{ 
    public object GetService(Type serviceType) 
    { 
     if (serviceType.IsAbstract || serviceType.IsInterface) 
     { 
      return ObjectFactory.TryGetInstance(serviceType); 
     } 
     else 
     { 
      return ObjectFactory.GetInstance(serviceType); 
     } 
    } 

    public IEnumerable<object> GetServices(Type serviceType) 
    { 
     foreach (object obj in ObjectFactory.GetAllInstances(serviceType)) 
     { 
      yield return obj; 
     } 
    } 
} 

lui-même cela ne résout toujours pas le problème jusqu'à ce que je supprime cette expression de configuration :

x.For<IControllerFactory>().Use<DefaultControllerFactory>(); 

Selon le TryGetInstance de documentation renvoie uniquement les types enregistrés avec le conteneur et renvoie null si aucune existe. Je suppose que le code MVC 3 repose sur ce comportement pour indiquer qu'il devrait utiliser ses valeurs par défaut, donc dans mon cas d'origine, j'ai dû enregistrer ces valeurs par défaut avec mon conteneur. Tricky un!

8

J'ai réussi à faire fonctionner StructureMap avec ASP.NET MVC3 en créant une classe Dependency Resolver (IDependencyResolver), puis en enregistrant cette classe dans global.asax. Je n'ai pas entièrement testé ce code. Mais, il a travaillé sans problèmes dans deux applications.

StructureMapDependencyResolver.cs

using System.Linq; 
using System.Web.Mvc; 
using StructureMap; 

namespace SomeNameSpace 
{ 
    public class StructureMapDependencyResolver : IDependencyResolver 
    { 
     private readonly IContainer container; 

     public StructureMapDependencyResolver(IContainer container) 
     { 
      this.container = container; 
     } 

     public object GetService(System.Type serviceType) 
     { 
      try 
      { 
       return this.container.GetInstance(serviceType); 
      } 
      catch 
      { 
       return null; 
      } 
     } 

     public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType) 
     { 
      return this.container.GetAllInstances<object>() 
       .Where(s => s.GetType() == serviceType); 
     } 
    } 
} 

Global.asax.cs

 public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
     { 
      filters.Add(new HandleErrorAttribute()); 
     } 

     public static void RegisterRoutes(RouteCollection routes) 
     { 
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

      routes.MapRoute(
       "Default", // Route name 
       "{controller}/{action}/{id}", // URL with parameters 
       new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 
      ); 

     } 

     protected void Application_Start() 
     { 
      AreaRegistration.RegisterAllAreas(); 

      DependencyResolver.SetResolver(new StructureMapDependencyResolver(InitContainer())); 
      RegisterGlobalFilters(GlobalFilters.Filters);    
      RegisterRoutes(RouteTable.Routes); 
     } 

     private static IContainer InitContainer() 
     { 
      ObjectFactory.Initialize(x => 
      { 
       x.Scan(y => 
       { 
        y.WithDefaultConventions(); 
        y.AssembliesFromApplicationBaseDirectory(); 
        y.LookForRegistries(); 
       }); 
      }); 

      return ObjectFactory.Container; 
     } 
+0

Voilà comment je l'ai mis en place aussi, et il fonctionne pour moi.Mais je ne suis pas sûr de savoir comment cela est différent des versions précédentes où nous avons utilisé une sous-classe de DefaultControllerFactory et qui a été définie dans un appel à la propriété Current de ControllerBuilder – Hakeem

+0

Travaillez-vous avec la version RTM? Avez-vous un code d'installation pertinent dans vos registres? Si j'exécute du code comme ci-dessus, j'obtiens une exception StructureMap indiquant qu'aucune instance par défaut d'IControllerFactory n'est définie. –

+0

Hakeem - Sous-classe DefaultControllerFactory ne nous a fourni que la prise en charge de l'injection de dépendances pour les contrôleurs. Dans MVC3, l'utilisation de DependencyResolver.SetResolver nous permet de prendre en charge l'injection de dépendances pour de nombreuses zones de la structure MVC, y compris les contrôleurs, les vues, les filtres d'action et les classeurs de modèle. –

0

Cela fonctionne pour moi à la fois MVC et Web API ..

namespace Web.Utilities.DependencyResolvers 
{ 
    public class StructureMapResolver : IServiceLocator, IDependencyResolver 
    { 
     private readonly IContainer _container; 

     public StructureMapResolver(IContainer container) 
     { 
      if (container == null) 
       throw new ArgumentNullException("container"); 

      this._container = container; 
     } 

     public IDependencyScope BeginScope() 
     { 
      return new StructureMapResolver(this._container.GetNestedContainer()); 
     } 

     public object GetInstance(Type serviceType, string instanceKey) 
     { 
      if (string.IsNullOrEmpty(instanceKey)) 
      { 
       return GetInstance(serviceType); 
      } 

      return this._container.GetInstance(serviceType, instanceKey); 
     } 

     public T GetInstance<T>() 
     { 
      return this._container.GetInstance<T>(); 
     } 

     public object GetService(Type serviceType) 
     { 
      return GetInstance(serviceType); 
     } 

     public IEnumerable<object> GetServices(Type serviceType) 
     { 
      return this._container.GetAllInstances(serviceType).Cast<object>(); 
     } 

     public T GetInstance<T>(string instanceKey) 
     { 
      return this._container.GetInstance<T>(instanceKey); 
     } 

     public object GetInstance(Type serviceType) 
     { 
      return serviceType.IsAbstract || serviceType.IsInterface ? 
       this._container.TryGetInstance(serviceType) : this._container.GetInstance(serviceType); 
     } 

     public IEnumerable<T> GetAllInstances<T>() 
     { 
      return this._container.GetAllInstances<T>(); 
     } 

     public IEnumerable<object> GetAllInstances(Type serviceType) 
     { 
      return this._container.GetAllInstances(serviceType).Cast<object>(); 
     } 

     public void Dispose() 
     { 
      this._container.Dispose(); 
     } 
    } 
} 
+0

Hey Alvin, bienvenue à SO. Normalement, cela vous aide beaucoup de laisser plus de détails dans votre réponse, surtout si vous ressuscitez un vieux sujet. Veuillez expliquer pourquoi votre réponse résout le problème. – 2rs2ts