8
votes

Ce code MVC peut-il être refactored à l'aide d'un modèle de conception?

J'ai un code de contrôleur comme celui-ci sur mon site ASP.NET MVC 3 Site: XXX

Fondamentalement, je parle du code dans le bloc de filetage. Toutes les choses doivent arriver, mais l'utilisateur n'a pas besoin de les attendre (bon boîtier pour un fil de fond, non?).

juste pour être clair, j'utilise la mise en cache (données ASP.NET régulières cache) surtout sur le site et la plupart d'entre eux a une politique de cache "non expire", alors j'exprime manuellement le cas lorsque cela est requis (comme ci-dessus).

et la partie utilisateur donne essentiellement un représentant de l'utilisateur pour Faire quelque chose (comme pile).

Récapitulat: nous avons la mise en cache, la gestion de la réputation de l'utilisateur, l'audit, tout en un. N'appartient pas vraiment à un endroit le fait. D'où le problème avec le code actuel et le problème d'essayer de déterminer comment le déplacer.

La raison pour laquelle je veux refracteur, c'est pour quelques raisons:

  1. Test difficile à l'unité. La multithreading et les tests unitaires ne jouent pas vraiment bien.
  2. lisibilité. Il est difficile de lire. Messy.
  3. SRP. Contrôleur faisant / en sachant trop.

    j'ai résolu 1) En enveloppant le code de frai de fil dans une interface, et simplement se moquer / simuler cela.

    Mais je voudrais faire une sorte de motif, où mon code pourrait ressembler à ceci: xxx

    essentiellement, mettez l'ONUS sur "quelque chose d'autre" à réagir, pas le contrôleur.

    J'ai entendu / lu À propos des tâches, des commandes, des événements, etc. mais n'ont pas encore vu l'un dans l'espace ASP.NET MVC.

    Premières pensées me dise de créer une sorte de "gestionnaire d'événements". Mais alors je pensais, où est-ce que ça va? Dans le domaine? Eh bien, comment gère-t-il les interactions avec le cache, qui est une infrastructure. Puis enfiler, ce qui est également une préoccupation d'infrastructure. Et si je veux faire est de synchrone, au lieu d'async? Qu'est-ce qui rend cette décision?

    Je ne veux pas avoir à empiler toute cette logique ailleurs. Il faut idéalement être récupéré à des composants gérables et significatifs, et non une responsabilité décalée, si cela a du sens.

    Tous les conseils?


1 commentaires

+1 C'est une bonne question à un problème commun. Je m'attends à voir les solutions N + 1 offertes. Où aller par la suite ... Hmmm :)


7 Réponses :


1
votes

Il existe une implémentation de bus de message dans MVCCONTRIB , dans le cadre de la fonctionnalité PORTABLEAREA. Son objectif principal est d'autoriser des fonctionnalités implémentées de manière indépendante à déclencher et à écouter des événements, ce qui semble à peu près comme ce que vous désirez.

Je ne sais pas si c'est la meilleure option là-bas, car l'état de MVCCONTRIB est quelque peu sans papiers et sommaire. Certaines parties sont activement maintenues alors que d'autres sont obsolètes.

Une autre option à considérer est Zeromq , mais cela peut être surchargé pour vos besoins.


0 commentaires

2
votes

Les premières pensées me disent de créer une sorte de "gestionnaire d'événements". Mais alors je pensais, où est-ce que ça va? Dans le domaine?

C'est comme ça que je résolvez le problème. Je vois le gestionnaire d'événements comme infrastructure. Mais les événements réels appartiennent au domaine.

Bien, comment gère-t-il les interactions avec le cache, qui est une préoccupation d'infrastructure? Puis enfiler, ce qui est également une préoccupation d'infrastructure. Et si je veux faire est de synchrone, au lieu d'async? Qu'est-ce qui fait cette décision?

Async est sympa, mais rend la manipulation de la transaction complexe. Si vous utilisez un conteneur COI, vous avez déjà une portée bien définie et une transaction pouvant être utilisée lors de la propagation des événements.

IMHO Il appartient à l'abonné de planifier / de filer sa tâche si elle sait que c'est l'événement La manipulation prendra du temps.

Solution proposée:

Utilisez votre conteneur de CIO pour publier les événements. Je laisserais le référentiel publier les événements ( postupdatedatedatedated ou entitéUPDated (code> en fonction de ce que vous souhaitez faire avec l'événement) plutôt que le contrôleur (pour réduire la duplication de code).

J'ai fait une implémentation du CIO pour Autofac qui vous permet de: xxx

abonnement: xxx

https://github.com/sogeti-se/sogeti. Motif / Wiki / Domain-Événements

Utilisation typique

  1. implémenter iserviceresolver (pour votre conteneur)
  2. Attribuez-le: Serviceresolver.Assign (Nouveau Yourresolver (YourContainer))
  3. Utilisez comme décrit ici .

9 commentaires

intéressant. Mais alors comment le filetage va-t-il dans le mélange? Le cache aurait-il besoin de tirer le fil?


type de. J'ai juste besoin d'avoir une pièce de théâtre lorsque je passe au travail et de comprendre comment faire cela avec mon conteneur DI (StructureMap). Restez à l'écoute...


En regardant votre code GitHub, on dirait que beaucoup de code pour moi d'essayer de passer à StructureMap. Connaissez-vous de tout nuge / ressources existant qui le faisait dans Structuremap? Je ne veux pas avoir à dépenser beaucoup de temps à rouler mon propre gestionnaire d'événements StructureMap.


Le seul code que vous avez à porter est cette classe: GITUB.COM/SODITIATIATIATIAD/SOGETI.PATTERN/BLOB/MASTER/SOURCE/... Vous pouvez utiliser le sogeti.pattern.code Paquet Nuget pour le reste.


Avez-vous n'importe où où vous avez documenté l'utilisation typique de cette bibliothèque dans une application MVC? Ne semble pas info / doco sur le site Nuget. J'ai installé le paquet, mais comment puis-je l'utiliser? Évidemment, j'ai besoin de faire des changements di, etc ... mais existe-t-il un exemple de site MVC que vous avez et que vous rencontrez avec SOGETI?


Entre-temps, j'ai trouvé cela, ce qui semble prometteur: CodeProject .Com / Articles / 52810 / ...


ok donc j'ai fait ce qui suit. 1) installé (via nuget) sogeti.pattern.core (0.0.4) . 2) Mise à jour mon résolveur SM pour implémenter iserviceresolver (et le configurez-le dans global.asax). 3) Création d'une classe simple qui implémente idomainevent . 4) Fabriqué un service de service IAutosubscriberof . Soulevé cet événement dans une action de contrôleur telle que décrite. Aucune erreur, mais le gestionnaire n'est pas appelé. Y a-t-il quelque chose que j'ai manqué ou y a-t-il quelque chose que je peux déboguer / vérifier les problèmes? Appréciez vraiment toute votre aide BTW. :)


J'ai attrapé la source GIT et j'ai ajouté le projet dans ma solution pour le débogage. C'est dans la méthode Serviceresolverdispatcher.Dispatch , mais Serviceresolver.Current.resolveall (type) retourne 0 articles. Où type est iAutosubscriberof . Je manque certainement quelque chose.


Nevermind, je l'ai travaillé. C'était un autre changement de structure que j'ai ajouté. Accepté et ajoutera une réponse séparée pour les utilisateurs de StructureMap.



2
votes

Peut-être que l'objet post devrait se mettre à jour et être passé un irpostage (qui a elle-même été transmise au contrôleur.) (Il s'agit d'injection de dépendance de base / IOC, et conserve le contrôleur SKINNIER)

//in controller:
var post = Mapper.Map<PostViewModel, Post>(viewModel);
post.Update(_postRepository);

//inside Post.cs:
public Update(IRepository rep){
//update db with the repo    
//give points
}


3 commentaires

A) Ensuite, le poste (domaine) a une dépendance sur le référentiel, qui, même si le référentiel est sans doute fait partie du domaine, j'aime garder mon travail de mon poco. b) ne résout pas vraiment le problème du cache.


@ RPM1984, c'est DDD où la logique de domaine est à l'intérieur du modèle.


@Darindimitrov - D'accord, je ne fais pas de DDD alors. :) Mais je veux mes pocos avec des propriétés et une logique commerciale (interne), mais ne nécessitant pas de dépendances externes.



2
votes

Vous pouvez utiliser Programmation orientée verso sur ce problème particulier. Le produit couramment utilisé dans le monde .net est postSharp .

L'idée serait que vous ajoutez un attribut au-dessus de la méthode. L'attribut indiquera quelles actions particulières doivent être effectuées (dans votre cache de cas de rafraîchissement, des points d'augmenter, etc.) et quand il devrait arriver (dans votre cas lors de la sortie de la méthode par exemple).

Vous pouvez également les séparer à différents attributs, de sorte que vous puissiez faire différents types de combinaisons.


0 commentaires

1
votes

Vous voudrez peut-être envisager d'implémenter un système de bus de service tel que NSERVICEBUS si vous avez affaire à un casse-cache ou à la vérification ( cela peut être mieux servi de manière asynchrone).

Avec un bus de messagerie, des messages d'événement peuvent être publiés sur n'importe quel nombre de gestionnaires d'événements (par exemple un gestionnaire de cache) de manière asynchrone, de sorte que votre application peut déclencher un message, puis continuer à servir rapidement des pages synchrones.


0 commentaires

0
votes
  1. Je pense que vous devez envisager transaction forte>. Si vous essayez de mettre à jour les caches dans un autre fil et de renvoyer le résultat d'action immédiatement après le référentiel.Save. Il est difficile de faire rouler si les opérations de cache jettent une erreur (la plupart des caches sont globales, qui sont généralement confrontées à des problèmes de verrouillage / synchronisation). LI>
  2. MVC est un motif de couche d'interface utilisateur. J'ai généralement placé une logique contextuelle HTTP dans le corps d'action et mettez la logique commerciale dans des couches inférieures, par exemple un service, qui gère le modèle de domaine. Dans votre cas, j'aimerais ajouter un iPostservice et mettre votre fonction de sauvegarde et votre fonction mise en cache. Qui est quelque chose comme: p>

    //codes in your PostService which implements IPostService
    [CacheEvent("POST")]
    [Transaction]
    public void Save(Post post) //care about domain model instead of view model
    {
       // Save.
       _postReposity.Save(post);
    }
    
  3. De plus, je pense qu'il est bon d'utiliser AOP pour déclencher un événement et d'utiliser un conteneur de COI pour injecter la logique de cache (ainsi que la logique de transaction). Quelque chose comme: p>

    [HttpPost]
    public ActionResult Save(PostViewModel viewModel)
    {
       // Map.
       var post = Mapper.Map<PostViewModel, Post>(viewModel);
    
       // Save.
       _postService.Save(post);
    
       // Redirect.
       return RedirectToAction("Index");
    }
    

1 commentaires

J'avais l'habitude d'avoir des services, mais je les ai déchirés parce que je me suis rendu compte qu'ils venaient d'envelopper mon référentiel et d'ajouter aucune valeur. Je ne pense pas que le fait de déplacer le cache il y a des mandats de retour. Merci pour la pointe cependant. tous les conseils utiles. :)



0
votes

La réponse de @ jgauuffine a accepté, mais je pensais ajouter une réponse séparée depuis que j'utilise Structuremap, et il avait besoin une étape supplémentaire .

J'ai suivi ce que @JGAuffin spécifié, mais ce n'est pas " t tisser mon gestionnaire d'événements.

Alors j'ai ajouté ceci à mon structureMap config: xxx

alors ça marche.

i ' M Devinant l'alternative à ce sujet est de spécifier manuellement chaque auditeur enregistré, qui obtient un peu fou.

Quelqu'un qui utilise Structuremap me dire s'il s'agit de la bonne approche?


0 commentaires