2009-09-26 4 views
11

J'ai examiné des applications plus simples comme Nerddinner et ContactManager ainsi que des applications plus complexes comme Kigg. Je comprends les plus simples et maintenant je voudrais comprendre les plus complexes.Passer mon MVC au niveau suivant: DI et unité de travail

Habituellement, les applications les plus simples ont des classes et des interfaces de référentiel (aussi faiblement couplées qu'elles peuvent l'être) au-dessus de LINQtoSQL ou d'Entity Framework. Les référentiels sont appelés à partir des contrôleurs pour effectuer les opérations de données nécessaires.

Un modèle commun que je vois quand j'examine des applications plus complexes comme Kigg ou Oxite est l'introduction de (je qu'effleurer la surface ici mais je dois commencer quelque part):

  • CIO DI (en Kigg de affaire Unity)
  • Web Demande gestionnaire de vie
  • Unité de travail

Voici mes questions:

Je comprends que pour avoir vraiment une application faiblement couplée, vous devez utiliser quelque chose comme Unity. Mais il semble également que le moment où vous présentez Unity au mixage, vous devez également introduire un gestionnaire de demande de vie Web. Pourquoi donc? Pourquoi est-ce que des exemples d'applications comme Nerddinner n'ont pas de gestionnaire de demande de vie Web? Qu'est-ce que cela fait exactement? Est-ce une chose spécifique à l'Unité?

Un deuxième motif que je remarque est l'introduction de l'unité de travail. Encore une fois, même question: Pourquoi Nerddinner ou ContactManager n'utilisent-ils pas Unit of Work? Au lieu de cela, ces applications utilisent les classes de référentiel au-dessus de Linq2Sql ou Entity Framework pour effectuer la manipulation des données. Aucun signe d'unité de travail. Qu'est-ce que c'est exactement et pourquoi devrait-il être utilisé?

Merci

Ci-dessous un exemple de DI en Nerddiner au niveau DinnersController:

public DinnersController() 
     : this(new DinnerRepository()) { 
    } 

    public DinnersController(IDinnerRepository repository) { 
     dinnerRepository = repository; 
    } 

Alors suis-je raison de supposer qu'en raison du premier constructeur du contrôleur « possède » le DinnerRepository et dépendra donc de la durée de vie du contrôleur puisqu'il y est déclaré?

Répondre

3

Avec Linq-to-SQL est utilisé directement, votre contrôleur possède la référence au contexte de données . C'est généralement une référence privée à l'intérieur du contrôleur, et est donc créé dans le cadre de sa construction. Il n'y a aucun besoin dans la gestion de la vie, puisque c'est dans un endroit. Toutefois, lorsque vous utilisez un conteneur IoC, votre référentiel de données est créé en dehors de votre contrôleur. Puisque le conteneur IoC qui le crée pour vous ne sait pas comment et pendant combien de temps vous allez utiliser l'objet créé, une stratégie à vie est introduite. Par exemple, le contexte de données (référentiel) est généralement créé au début de la requête Web et détruit à la fin. Toutefois, pour les composants qui fonctionnent avec un service Web externe ou un mappeur statique (par exemple, un enregistreur), il n'est pas nécessaire de les créer à chaque fois. Donc, vous voudrez peut-être dire de les créer une fois (c'est-à-dire le style de vie singletone). Tout cela arrive parce que les conteneurs IoC (comme Unity) sont conçus pour gérer de nombreuses situations, et ils ne connaissent pas vos besoins spécifiques. Par exemple, certaines applications utilisent des transactions "conversation" où NHibernate (ou Entity Framework peut-être) peut durer pendant plusieurs pages/requêtes web. Les conteneurs IoC vous permettent de modifier la durée de vie des objets en fonction de vos besoins. Mais comme cela est dit au prix - puisqu'il n'y a pas de stratégie prédéfinie, vous devez en sélectionner une vous-même. Pourquoi NerdDinner et d'autres applications n'utilisent pas de techniques plus avancées simplement parce qu'elles sont destinées à démontrer les fonctionnalités MVC, et non les utilisations avancées de certaines autres bibliothèques. Je me souviens d'un article écrit pour démontrer une fonctionnalité avancée de conteneur IoC - cet article a cassé quelques modèles de conception approuvés comme la séparation des soucis - mais ce n'était pas si important parce que les modèles de conception n'étaient pas le but de l'article. Même chose avec de simples applications de démonstration MVC - ils ne veulent pas que vous, le nouveau venu MVC, soit perdu dans les labyrinthes de l'IoC.

Je recommande de ne pas regarder Oxite comme un exemple de référence de conception: http://codebetter.com/blogs/karlseguin/archive/2008/12/15/oxite-oh-dear-lord-why.aspx http://ayende.com/Blog/archive/2008/12/19/oxite-open-exchangable-informative-troubled-engine.aspx

+0

Merci! Cela a aidé. J'ai édité ma question en bas. Est-ce ce que vous voulez dire quand vous dites que le contrôleur possède la référence au contexte du référentiel/données? – Thomas

+0

Pas exactement. Dans NerdDinner, ils utilisent un constructeur supplémentaire acceptant IDinnerRepository pour faciliter les tests unitaires. Mais oui, il s'agit toujours du contrôleur (constructeur sans paramètre) ou des tests qui créent et possèdent l'objet du référentiel. Ils meurent tous les deux et il n'y a pas d'autres utilisateurs du référentiel; donc la vie est simple. En passant, cette technique est mauvaise. vous pouvez en lire plus à ce sujet ici: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/07/03/how-not-to-do-dependency-injection-in-nerddinner.aspx (ainsi comme google pour "l'IoC du pauvre"). – queen3

+0

L'argument de Jimmy Bogard à propos de cet exemple flagrant d '«IoC du pauvre» est extrêmement bon ici. Les commentaires sont également bons. Vaut vraiment le coup d'être lu. –

0

La plupart des conteneurs DI, sinon tous, touchent au concept de durée de vie, je crois. Selon le scénario concerné, vous souhaiterez peut-être que le conteneur renvoie toujours la même instance d'un composant enregistré, tandis que pour un autre composant, vous souhaiterez peut-être toujours renvoyer une nouvelle instance. La plupart des conteneurs vous permettent également de spécifier que dans un contexte particulier, vous voulez qu'il retourne la même instance, etc.

Je ne connais pas très bien l'unité (jusqu'à présent, j'ai utilisé Windsor et Autofac), mais je soupçonner que le gestionnaire de durée de vie de demande Web est une implémentation de stratégies à vie où la même instance est fournie par le conteneur pendant la durée de vie d'une seule requête Web. Vous trouverez des stratégies similaires dans des conteneurs comme Windsor.

Enfin, je suppose que vous faites référence à l'unité de travail. Une unité de travail est essentiellement un groupe d'actions que vous voulez réussir ou échouer en tant qu'une transaction commerciale atomique. Pour une description plus formelle, regardez definition de Martin Fowler. C'est un concept qui a gagné en popularité dans le contexte de Domain Driven Design. Une unité de travail conserve une trace des changements que vous appliquez dans une telle transaction, et lorsque le moment est venu, elle valide ces changements dans une transaction ACID. Dans NHibernate, par exemple, la session supporte la notion d'unité de travail et plus spécifiquement le suivi des changements, alors que dans Linq2SQL c'est le Contexte ...

Questions connexes