2012-02-13 3 views
2

Je crée un ActionResult personnalisé pour mes contrôleurs car j'ai remarqué que beaucoup de code répété pouvait être réutilisé. Il ressemble à ceci:Injection de dépendances lors de la construction de l'objet

public ExtendedViewResult<T> : ActionResult 
{ 
     protected T Model { get; set; } 
     protected IModelExtender<T> Extender { get; set; } 

     public ExtendedActionResult(T model, IModelExtender<T> extender) 
     { 
      this.Model = model; 
      this.Extender = extender; 
     } 
} 

public class BaseController : Controller 
{ 
    public ExtendedViewResult<T> ExtendedView<T>(T model) 
    { 
     // I need to create the result here, but how? 
     var result = new ExtendedViewResult<T>(model, ???????); 

     return result; 
    } 
} 

Le problème que j'ai est que je ne suis pas sûr de savoir comment construire mon objet ExtendedViewResult. Comme l'interface est générique, je veux utiliser Dependency Injection pour obtenir l'objet approprié, mais je ne suis pas sûr de savoir comment le faire puisque je construis l'objet moi-même.

J'utilise Ninject et Ninject.MVC3 et la valeur par défaut package Nuget crée une classe Bootstrapper pour moi, et quand j'accéder à la propriété Bootstrapper.Kernel je reçois l'avertissement suivant:

Ninject.Web.Mvc.Bootstrapper.Kernel is obsolete. Do not use Ninject as Service Locator.

Si je Je ne suis pas supposé accéder au noyau directement, alors comment puis-je changer mon code pour pouvoir obtenir la classe concrète appropriée?

EDIT
Voici le code ninject bootstrapper. La seule méthode que j'ai ajoutée est le GetInstance()

public static class NinjectMVC3 
{ 
    private static readonly Bootstrapper bootstrapper = new Bootstrapper(); 

    /// <summary> 
    /// Starts the application 
    /// </summary> 
    public static void Start() 
    { 
     DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule)); 
     DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule)); 
     bootstrapper.Initialize(CreateKernel); 
    } 

    /// <summary> 
    /// Stops the application. 
    /// </summary> 
    public static void Stop() 
    { 
     bootstrapper.ShutDown(); 
    } 

    // I ADDED THIS CODE, EVERYTHING ELSE IS AUTO-GENERATED 
    // BY THE NUGET PACKAGE 
    public static T GetInstance<T>() 
    { 
     return bootstrapper.Kernel.Get<T>(); 
    } 

    /// <summary> 
    /// Creates the kernel that will manage your application. 
    /// </summary> 
    /// <returns>The created kernel.</returns> 
    private static IKernel CreateKernel() 
    { 
     var kernel = new StandardKernel(); 
     RegisterServices(kernel); 
     return kernel; 
    } 

    /// <summary> 
    /// Load your modules or register your services here! 
    /// </summary> 
    /// <param name="kernel">The kernel.</param> 
    private static void RegisterServices(IKernel kernel) 
    { 
    }   
} 

Répondre

1

Désolé ce sera sec sur le code réel - je n'ai pas beaucoup utilisé ninject, mais d'autres conteneurs DI ont cette solution, et je suis sûr que ninject aura cette construction aussi bien.

Le problème est que vous construisez l'objet lui-même. Lorsque vous utilisez réellement un conteneur DI et l'IoC suivant, chaque fois que vous voyez le mot-clé new devrait être un drapeau rouge - et l'utilisation du conteneur en tant que localisateur de service est un jaune.

Alors, comment pouvons-nous nous débarrasser de la «nouvelle», puisque vous avez besoin d'un nouvel objet? La réponse est d'avoir votre BaseController prendre une dépendance sur une usine qui peut créer un ExtendedViewResult. En Autofac (mon conteneur de choix), ce serait aussi simple que d'avoir un Func<ExtendedViewResult> injecté. Je serais surpris si Ninject n'a pas la même chose. En fait, il ressemble à ce qu'il fait - this ninject wiki page points à ce blog post on Ninject.Extensions.Factory.

Cela signifie donc que votre code pour le contrôleur pourrait ressembler à ceci:

public class ConcreteController : BaseController 
{ 
    private Func<Foo,ExtendedViewResult<Foo>> _factory; 
    public BaseController(Func<Foo,ExtendedViewResult<Foo>> factory) 
    { 
     _factory = factory; 
    } 

    public ExtendedViewResult<Foo> Method(Foo model) 
    {    
     var result = _factory(model);  
     return result; 
    } 
} 

Si vous voulez vraiment avoir une méthode générique ne la création dans votre classe de base, alors vous aurez probablement besoin d'aller explicite itinéraire d'interface d'usine de l'article de blog lié ci-dessus. Avec ce code de style cependant, vous n'en auriez pas besoin dans la plupart des cas, et vos contrôleurs déclarent explicitement les dépendances dont ils ont besoin.

+0

Le problème avec le passage dans le contrôleur concret est que je pourrais avoir 10 différentes méthodes d'action qui ont tous des modèles différents. Pour ce faire, je devrais passer 10 dépendances Func différentes à mon contrôleur, ce qui ne semble pas être la meilleure option. – Dismissile

+0

@Dississile Dans ce cas, je recommanderais de créer vous-même une usine. Je ne pense pas que ninject puisse créer automatiquement des usines basées sur un générique ouvert, et le paramètre constructeur le rend plus difficile.Si vous trouvez que c'est possible, veuillez mettre à jour votre question, éditer cette réponse ou répondre à votre propre question - J'aimerais voir le résultat. Mon conteneur de choix est AutoFac, et je pense qu'il faudrait un peu de magie de registrationsource pour y parvenir. –

+0

@Dississile concernant votre propre usine, n'oubliez pas que vous pouvez prendre une dépendance sur IResolutionRoot et appeler .Get sur cela. Dans le cas simple, vous pourriez simplement prendre une dépendance sur IResolutionRoot dans votre BaseController. Ceci utilise à peu près un ServiceLocator, mais vous demandez le locator en tant que dépendance plutôt que de le trouver vous-même. –