J'ai un processus d'enregistrement en plusieurs étapes , soutenu par un seul objet dans la couche de domaine , qui a des règles de validation définies sur les propriétés.
Comment valider l'objet de domaine lorsque le domaine est divisé en plusieurs vues et que je dois enregistrer l'objet partiellement dans la première vue lors de sa publication?
J'ai pensé à utiliser Sessions mais ce n'est pas possible car le processus est long et la quantité de données est élevée, donc je ne veux pas utiliser session.
J'ai pensé à enregistrer toutes les données dans une base de données relationnelle en mémoire (avec le même schéma que la base de données principale), puis à vider ces données dans la base de données principale, mais des problèmes sont survenus car je devrais acheminer entre les services (demandés dans les vues) qui travaillent avec le base de données principale et base de données en mémoire.
Je recherche une solution élégante et propre (plus précisément une meilleure pratique).
MISE À JOUR ET Clarification:
@Darin Merci pour votre réponse réfléchie, c'est exactement ce que j'ai fait jusqu'à présent. Mais accessoirement, j'ai une demande qui contient de nombreuses pièces jointes, je conçois un Step2View
exemple, quel utilisateur peut télécharger des documents de manière asynchrone, mais ces pièces jointes doivent être enregistrées dans une table avec une relation référentielle avec une autre table qui aurait dû être enregistrée auparavant dans Step1View
.
Ainsi, je devrais enregistrer l'objet de domaine dans Step1
(partiellement), mais je ne peux pas, car l'objet de domaine principal sauvegardé qui est mappé partiellement à ViewModel de Step1 ne peut pas être enregistré sans les accessoires qui proviennent de convertis Step2ViewModel
.
la source
Réponses:
Tout d'abord, vous ne devez utiliser aucun objet de domaine dans vos vues. Vous devriez utiliser des modèles de vue. Chaque modèle de vue contiendra uniquement les propriétés requises par la vue donnée ainsi que les attributs de validation spécifiques à cette vue donnée. Donc, si vous avez un assistant en 3 étapes, cela signifie que vous aurez 3 modèles de vue, un pour chaque étape:
etc. Tous ces modèles de vue peuvent être soutenus par un modèle de vue principal de l'assistant:
alors vous pourriez avoir des actions de contrôleur rendant chaque étape du processus de l'assistant et passant le principal
WizardViewModel
à la vue. Lorsque vous êtes à la première étape de l'action du contrôleur, vous pouvez initialiser laStep1
propriété. Ensuite, dans la vue, vous générez le formulaire permettant à l'utilisateur de remplir les propriétés de l'étape 1. Lorsque le formulaire est soumis, l'action du contrôleur appliquera les règles de validation pour l'étape 1 uniquement:Maintenant, dans la vue de l'étape 2, vous pouvez utiliser l' assistant Html.Serialize des futurs MVC afin de sérialiser l'étape 1 dans un champ caché à l'intérieur du formulaire (sorte de ViewState si vous le souhaitez):
et à l'intérieur de l'action POST de l'étape 2:
Et ainsi de suite jusqu'à ce que vous arriviez à la dernière étape où vous aurez
WizardViewModel
rempli toutes les données. Ensuite, vous allez mapper le modèle de vue sur votre modèle de domaine et le transmettre à la couche de service pour traitement. La couche de service peut exécuter toutes les règles de validation elle-même et ainsi de suite ...Il existe également une autre alternative: utiliser javascript et mettre tout sur la même page. Il existe de nombreux plugins jquery qui fournissent des fonctionnalités d'assistant ( Stepy est un bon). Il s'agit essentiellement d'afficher et de masquer les divs sur le client, auquel cas vous n'avez plus à vous soucier de l'état persistant entre les étapes.
Mais quelle que soit la solution que vous choisissez, utilisez toujours des modèles de vue et effectuez la validation sur ces modèles de vue. Tant que vous collerez des attributs de validation d'annotation de données sur vos modèles de domaine, vous aurez beaucoup de mal car les modèles de domaine ne sont pas adaptés aux vues.
METTRE À JOUR:
OK, en raison des nombreux commentaires, je tire la conclusion que ma réponse n'était pas claire. Et je dois être d'accord. Alors laissez-moi essayer de développer davantage mon exemple.
Nous pourrions définir une interface que tous les modèles de vue pas à pas devraient implémenter (c'est juste une interface de marqueur):
puis nous définirions 3 étapes pour l'assistant où chaque étape ne contiendrait bien sûr que les propriétés dont elle a besoin ainsi que les attributs de validation pertinents:
Ensuite, nous définissons le modèle de vue de l'assistant principal qui se compose d'une liste d'étapes et d'un index d'étape actuel:
Ensuite, nous passons au contrôleur:
Quelques remarques sur ce contrôleur:
[Deserialize]
attributs de la bibliothèque Microsoft Futures afin de vous assurer que vous avez installéMvcContrib
NuGet. C'est la raison pour laquelle les modèles de vue doivent être décorés avec l'[Serializable]
attributIStepViewModel
interface, donc pour que cela ait un sens, nous avons besoin d'un classeur de modèles personnalisé.Voici le classeur de modèles associé:
Ce classeur utilise un champ caché spécial appelé StepType qui contiendra le type concret de chaque étape et que nous enverrons à chaque demande.
Ce modèle de classeur sera enregistré dans
Application_Start
:Le dernier élément manquant du puzzle sont les vues. Voici la
~/Views/Wizard/Index.cshtml
vue principale :Et c'est tout ce dont vous avez besoin pour que cela fonctionne. Bien sûr, si vous le souhaitez, vous pouvez personnaliser l'apparence de certaines ou de toutes les étapes de l'assistant en définissant un modèle d'éditeur personnalisé. Par exemple, faisons-le pour l'étape 2. Nous définissons donc un
~/Views/Wizard/EditorTemplates/Step2ViewModel.cshtml
partiel:Voici à quoi ressemble la structure:
Bien entendu, il y a place à amélioration. L'action Index POST ressemble à s..t. Il y a trop de code dedans. Une simplification supplémentaire impliquerait de déplacer tous les éléments de l'infrastructure comme l'index, la gestion actuelle des index, la copie de l'étape actuelle dans l'assistant, ... dans un autre classeur de modèles. Alors que finalement nous nous retrouvons avec:
c'est plutôt à quoi devraient ressembler les actions POST. Je laisse cette amélioration pour la prochaine fois :-)
la source
Pour compléter la réponse d'Amit Bagga, vous trouverez ci-dessous ce que j'ai fait. Même si c'est moins élégant, je trouve cette voie plus simple que la réponse de Darin.
Manette :
Des modèles :
la source
Je vous suggère de maintenir l'état du processus complet sur le client à l'aide de Jquery.
De cette façon, vous pouvez facilement créer votre objet de domaine directement à partir des données de publication du formulaire et au cas où les données contiendraient des erreurs, retournez un JSON valide contenant tous les messages d'erreur et les afficher dans un div.
Veuillez diviser les étapes
la source
Les assistants ne sont que de simples étapes de traitement d'un modèle simple. Il n'y a aucune raison de créer plusieurs modèles pour un assistant. Tout ce que vous feriez est de créer un modèle unique et de le transmettre entre les actions dans un seul contrôleur.
L'étudiante ci-dessus est stupide, alors remplacez vos champs là-dedans. Ensuite, nous commençons par une action simple qui lance notre assistant.
Cela appelle la vue "WizardStep1.cshtml (si vous utilisez un rasoir). Vous pouvez utiliser l'assistant de création de modèle si vous le souhaitez. Nous redirigerons simplement le message vers une action différente.
La chose à noter est que nous publierons ceci dans une action différente; l'action WizardStep2
Dans cette action, nous vérifions si notre modèle est valide, et si c'est le cas, nous l'envoyons à notre vue WizardStep2.cshtml, sinon nous le renvoyons à la première étape avec les erreurs de validation. À chaque étape, nous l'envoyons à l'étape suivante, validons cette étape et passons à autre chose. Maintenant, certains développeurs avertis pourraient bien dire que nous ne pouvons pas passer d'une étape à l'autre si nous utilisons des attributs [Obligatoire] ou d'autres annotations de données entre les étapes. Et vous auriez raison, alors supprimez les erreurs sur les éléments qui doivent encore être vérifiés. comme ci-dessous.
Enfin, nous enregistrerions le modèle une fois dans le magasin de données. Cela empêche également un utilisateur qui démarre un assistant mais ne le termine pas de ne pas enregistrer des données incomplètes dans la base de données.
J'espère que vous trouverez cette méthode d'implémentation d'un assistant beaucoup plus facile à utiliser et à maintenir que n'importe laquelle des méthodes mentionnées précédemment.
Merci d'avoir lu.
la source
Je voulais partager ma propre façon de gérer ces exigences. Je ne voulais pas du tout utiliser SessionState, je ne voulais pas non plus qu'il soit géré côté client, et la méthode de sérialisation nécessite MVC Futures que je ne voulais pas avoir à inclure dans mon projet.
Au lieu de cela, j'ai construit un HTML Helper qui parcourra toutes les propriétés du modèle et générera un élément caché personnalisé pour chacune. S'il s'agit d'une propriété complexe, elle s'exécutera de manière récursive dessus.
Dans votre formulaire, ils seront publiés sur le contrôleur avec les nouvelles données du modèle à chaque étape de "l'assistant".
J'ai écrit ceci pour MVC 5.
Maintenant, pour toutes les étapes de votre "assistant", vous pouvez utiliser le même modèle de base et transmettre les propriétés du modèle "Étape 1, 2, 3" à l'aide @ Html.HiddenClassFor en utilisant une expression lambda.
Vous pouvez même avoir un bouton de retour à chaque étape si vous le souhaitez. Il suffit d'avoir un bouton de retour dans votre formulaire qui le publiera dans une action StepNBack sur le contrôleur en utilisant l'attribut formaction. Non inclus dans l'exemple ci-dessous mais juste une idée pour vous.
Quoi qu'il en soit, voici un exemple de base:
Voici votre modèle
Voici votre CONTRÔLEUR
Voici vos VUES
Étape 1
Étape 2
Étape 3
la source
Ajout de plus d'informations à partir de la réponse de @ Darin.
Que se passe-t-il si vous avez un style de conception distinct pour chaque étape et que vous souhaitez conserver chacune dans une vue partielle distincte ou que faire si vous avez plusieurs propriétés pour chaque étape?
Lors de l'utilisation,
Html.EditorFor
nous avons la limitation d'utiliser la vue partielle.Créez 3 vues partielles sous le
Shared
dossier nommé:Step1ViewModel.cshtml , Step3ViewModel.cshtml , Step3ViewModel.cshtml
Par souci de concision, je viens de poster la 1ère vue patiale, les autres étapes sont identiques à la réponse de Darin.
Step1ViewModel.cs
Step1ViewModel.cshtml
Index.cshtml
S'il existe une meilleure solution, veuillez commenter pour en informer les autres.
la source
Une option consiste à créer un ensemble de tables identiques qui stockeront les données collectées à chaque étape. Ensuite, dans la dernière étape, si tout se passe bien, vous pouvez créer l'entité réelle en copiant les données temporaires et les stocker.
L'autre est de créer
Value Objects
pour chaque étape et de stocker ensuite dansCache
ouSession
. Ensuite, si tout se passe bien, vous pouvez créer votre objet Domaine à partir d'eux et l'enregistrerla source