2009-07-21 8 views
7

Je suis en train de concevoir un site web de taille moyenne en utilisant la technologie asp.net mvc. Toute la logique métier est organisée en services IS (tels que IDomainService, IUserService, IAuthService, ITrainingService). Tous les services utilisent IRepositories. J'utilise Ninject 1.5 pour câbler des services avec des contrôleurs et cela semble fonctionner parfaitement.Architecturer taille moyenne asp mvc - utiliser ninject et créer des objets

Il ya tellement de sujet que je n'ai aucune idée de la façon de gérer. Certains services créent des contextes (par requête) - par exemple IDomainService crée DomainContext (par requête) qui est nécessaire pour IUserService. ITrainingService est utilisé uniquement dans TrainingController, accessible uniquement aux utilisateurs autorisés, et ITrainingService requiert UserContext (également par requête) pour savoir qui a suivi une formation.

Ceci est mon premier projet utilisant un conteneur IoC. Existe-t-il un modèle de conception ou un schéma de code pour le résoudre? Je pense que je peux remplir l'objet de contexte en utilisant ActionFilters mais comment gérer sa durée de vie et où les rendre accessibles pour IServices? (d'une manière ellegant)

Répondre

10

J'ai utilisé Ninject spécifiquement dans une application MVC. La façon dont vous accompliriez ceci avec Ninject est dans la configuration ou la liaison de vos dépendances. Lorsque vous faites cela, vous spécifiez comment vous souhaitez gérer les durées de vie de vos objets. Dans la plupart des cas d'une application web, vos objets seront par requête comme vous l'avez indiqué dans votre question.

Une chose que j'ai remarqué dans votre question est que votre DomainContext est créé par un objet IDomainService et est utilisé par d'autres objets. Si l'objet de service de domaine est une sorte d'usine pour un DomainContext, alors vous n'avez pas beaucoup de problème - cela devient un exercice de la façon dont vous configurez Ninject pour fournir des objets concrets et injecter des dépendances.

Voici des conseils généraux sur la façon dont vous structurer votre application - garder à l'esprit que je n'ai pas la pleine compréhension de vos interfaces et classes:

public class GlobalApplication : NinjectHttpApplication { 
    protected override void RegisterRoutes(RouteCollection routes) { 

    // Your normal route registration goes here ... 

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

    routes.MapRoute(
     "Default",            
     "{controller}/{action}/{id}",       
     new { controller = "Home", action = "Index", id = "" } 
    ); 

    } 

    // This function is resposible for creating a Ninject kernel. This is where 
    // the magic starts to happen. 
    protected override IKernel CreateKernel() { 
    var modules = new IModule[] { 
            new AutoWiringModule(), 
            new AutoControllerModule(
             Assembly.GetExecutingAssembly()), 
            new ServiceModule() 
           }; 

    return new StandardKernel(modules); 
    } 
} 

note ci-dessus que la meilleure façon d'obtenir Ninject travailler est de dériver votre classe d'application à partir de la classe NinjectHttpApplication. Vous devrez modifier votre RegisterRoutes à une méthode de remplacement et sera également nécessaire pour implémenter une méthode appelée CreateKernel. La méthode CreateKernel est chargée de renvoyer le noyau Ninject qui est lui-même le conteneur IoC.

Dans le procédé CreateKernel, les ensembles Ninject fournis AutoControllerModule balayages pour les classes de contrôleur MVC et les enregistre avec le récipient. Cela signifie que les dépendances sur ces contrôleurs peuvent maintenant être injectées par Ninject car il est devenu le fournisseur du contrôleur pour l'application. La classe ServiceModule est celle que vous devez créer pour enregistrer tous vos services avec Ninject. Je devine qu'il ressemblerait à quelque chose comme ceci:

internal class ServiceModule : StandardModule { 
    public override void Load() { 
    Bind<IDomainService>() 
     .To<MyDomainService>() 
     .Using<OnePerRequestBehavior>(); 

    Bind<DomainContext>() 
     .ToMethod(ctx => ctx.Kernel.Get<IDomainService>().CurrentDomainContext) 
     .Using<OnePerRequestBehavior>(); 

    Bind<IService>() 
     .To<MyServiceType>() 
     .Using<OnePerRequestBehavior>(); 
    } 
} 

Ninject a obtenu une interface de Fluent assez expressif pour la configuration. Notez ci-dessus que chaque instruction associe fondamentalement une classe concrète avec une interface qu'elle implémente. La phrase "Using" dans l'instruction indique au noyau Ninject que l'objet ne vivra que pendant la durée de la requête. Ainsi, par exemple, cela signifie qu'à chaque fois qu'un objet IDomainService est demandé au noyau Ninject lors de la même requête, le même objet sera retourné. Pour ce qui est de vos objets de contexte, je constate que votre service de domaine crée ces contextes et agit comme une sorte de fabrique. À cet égard, j'ai lié instances DomainContext classes ci-dessus à produire en obtenant la valeur d'une propriété appelée CurrentDomainContext sur le IDomainService. C'est ce que le lambda ci-dessus accomplit. La bonne chose à propos de la liaison "ToMethod" dans Ninject est que vous avez accès à un objet de contexte d'activation Ninject qui vous permet de résoudre des objets en utilisant le noyau. C'est exactement ce que nous faisons pour obtenir le contexte actuel du domaine. Les étapes suivantes consistent à s'assurer que vos objets acceptent correctement les dépendances. Par exemple, vous dites que ITrainingService est utilisé uniquement dans la classe TrainingController. Donc, dans ce cas, je m'assurerais que TrainingController a un constructeur qui accepte un paramètre ITrainingService. Dans ce constructeur, vous pouvez enregistrer la référence au ITrainingService dans une variable membre. Comme dans:

public class TrainingController : Controller { 
    private readonly ITrainingService trainingService; 

    public TrainingController(ITrainingService trainingService) { 
    this.trainingService = trainingService; 
    } 

    // ... rest of controller implementation ... 
} 

Rappelez-vous que Ninject a déjà enregistré tous vos contrôleurs avec le noyau Ninject, alors quand ce contrôleur est créé et il est des actions sont invoquées, vous aurez une référence à la ITrainingService par de la variable membre trainingService.

J'espère que cela vous aide. L'utilisation de conteneurs IoC peut parfois devenir très confuse. Notez, je vous recommande vivement de consulter le Ninject documentation - c'est une introduction très bien écrite à Ninject ainsi que des concepts DI/IoC. J'ai également laissé de côté la discussion de l'AutoWiringModule montré ci-dessus; cependant, Nate Kohari (le créateur de Ninject) a a good write-up sur son blog à propos de cette fonctionnalité.

Bonne chance!

0

Je ne sais pas exactement si je comprends votre problème complètement, j'espère que ce petit conseil peut aider. Lors de l'utilisation d'un conteneur IoC, vous laissez le conteneur gérer la gestion de la vie d'un objet. J'ai seulement utilisé Castle Windsor et StructureMap pour l'injection de dépendance, donc je ne peux pas vous donner un exemple concret pour faire cela avec Ninject.

En regardant à travers la documentation Ninject, je pense que vous voulez regarder Activation Behaviours pour spécifier la gestion de la durée de vie de l'objet.

Espérons que cela aide.

+0

Lien brisé, conseil largement dépassé par la réponse complète de Peter. (Un bit particulièrement faux est de suggérer des comportements d'activation (le InRequestScope dans la réponse de Peter est la bonne approche)). Suggère de supprimer cette réponse. (Pas si mal que ça, je ne suis pas loin mais pas très loin) –