ASP.Net MVC et state - comment maintenir l'état entre les requêtes
comme une personne assez expérimentée ASP.Net développeur qui vient tout juste de commencer à utiliser MVC, je me retrouve à lutter un peu pour changer mon état d'esprit d'une manière traditionnelle "contrôle du serveur et gestionnaire d'événements" de faire des choses, dans le MVC plus dynamique des choses. Je pense que j'y arrive lentement, mais parfois la "magie" du MVC me déconcentre.
mon scénario actuel est de créer une page web qui permet à l'utilisateur de parcourir un fichier local, de le télécharger sur le serveur et de le répéter jusqu'à ce qu'il ait liste des fichiers pour travailler avec. Quand il est satisfait de la liste des fichiers (qui sera affichée dans une grille sur la page), il cliquera sur un bouton pour traiter les fichiers et extraire certaines données qui seront stockées dans une base de données.
la dernière partie n'est pas si importante, en ce moment, je me bats avec quelque chose d'aussi trivial que la création d'une liste de fichiers, et la persistance de cette liste entre les requêtes. Dans l'approche traditionnelle, cela serait extrêmement simple - les données seraient maintenues dans ViewState. Mais dans MVC j'ai besoin de passer les données entre le contrôleur et les vues, et je ne comprends pas complètement comment cela est supposé fonctionner.
je suppose que je ferais mieux de poster mon incomplètes tentative de codage pour expliquer le problème.
afin de conserver les données de ma liste de fichiers, j'ai créé un viewmodel qui est essentiellement une liste dactylographiée de fichiers, avec quelques métadonnées supplémentaires:
public class ImportDataViewModel
{
public ImportDataViewModel()
{
Files = new List<ImportDataFile>();
}
public List<ImportDataFile> Files { get; set; }
...
dans la vue, j'ai un formulaire pour parcourir et télécharger le fichier:
<form action="AddImportFile" method="post" enctype="multipart/form-data">
<label for="file">
Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
Le point de vue est à l'aide de ce dernier comme son modèle:
@model MHP.ViewModels.ImportDataViewModel
envoyer le fichier à mon action:
public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData)
{
if (file.ContentLength > 0)
{
ImportDataFile idFile = new ImportDataFile { File = file };
importData.Files.Add(idFile);
}
return View("DataImport", importData);
}
cette action renvoie la vue de la page D'Import de données avec l'instance viewmodel contenant la liste des fichiers.
cela fonctionne bien jusqu'à un certain point, je peux parcourir un fichier et le télécharger, et je peux voir les données viewmodel à l'intérieur de l'action, et puis aussi si je mets un point de rupture à l'intérieur de la vue et déboguer "ce.Modèle", tout va bien.
mais alors, si j'essaie de télécharger un autre fichier, en mettant un point de rupture dans L'action AddImportFile, le paramètre importData est vide. Donc la vue ne passe évidemment pas l'instance actuelle de son modèle à l'action.
dans les samples MVC que j'ai traversé, l'instance du modèle est "magiquement" passée à la méthode d'action comme paramètre, alors pourquoi est-elle vide maintenant?
je suppose que le vrai problème est mon limité la compréhension de MVC, et qu'il y a probablement une solution très simple à cela. De toute façon, je vous serais extrêmement reconnaissant si quelqu'un pouvait m'indiquer la bonne direction.
2 réponses
Cela fait un certain temps que j'ai posté la question, qui était assez colorée par ma petite expérience et connaissance de MVC. J'ai quand même reçu des informations très utiles, qui m'ont finalement amené à trouver une solution et aussi à acquérir une certaine connaissance de MVC.
Ce qui m'a jeté en premier lieu, est que vous pourriez avoir un contrôleur avec un objet fortement typé comme un paramètre, comme ceci:
public ActionResult DoSomething(MyClass myObject)...
cet objet provient du même contrôleur:
...
return View(myObject);
...
cela me porte à croire que l'objet a vécu tout au long de ces deux étapes, et que je pouvais d'une certaine façon m'attendre à ce que vous puissiez l'Envoyer à la vue, faire quelque chose et ensuite "magiquement" le rendre au contrôleur à nouveau.
après avoir lu sur la liaison des modèles, j'ai compris que ce n'était pas le cas. La vue est complètement morte et statique, et à moins que vous stockez l'information quelque part, c'est fini.
revenir à l' problème, qui était de sélectionner et de télécharger des fichiers à partir du client, et de construire une liste de ces fichiers à afficher, je me suis rendu compte qu'en général, il ya trois façons de stocker l'information entre les demandes dans MVC:
- vous pouvez stocker des informations dans des champs de formulaire dans la vue, et les afficher à nouveau sur le contrôleur plus tard
- Vous pouvez persister dans une sorte de stockage, par exemple un fichier ou une base de données
- vous pouvez le stocker dans la mémoire du serveur en accédant à des objets qui vit tout au long de demandes, par exemple, les variables de session
dans mon cas, j'avais essentiellement deux types d'informations à persister: 1. Les métadonnées du fichier (Nom du Fichier, taille du fichier, etc.) 2. Le contenu du fichier
"par le livre" approche serait probablement pour stocker les métadonnées dans les champs de formulaire et le contenu du fichier dans un fichier ou dans la db. Mais il y a aussi une autre façon. Comme je sais que mes fichiers sont assez petits et il n'y en aura que quelques-uns, et cette solution ne sera jamais être déployé dans une ferme de serveurs ou similaire, je voulais explorer l'option #3 des variables de session. Les fichiers ne sont pas non plus intéressants pour persister au - delà de la session-ils sont traités et jetés, donc je ne voulais pas les stocker dans mon db.
après avoir lu cet excellent article: accès ASP.NET données de Session utilisant la dynamique
j'étais convaincu. J'ai simplement créé une classe de sac de session comme décrit dans l'article, et puis je pourrais faire ce qui suit en mon controller:
[HttpPost]
public ActionResult AddImportFile(HttpPostedFileBase file)
{
ImportDataViewModel importData = SessionBag.Current.ImportData;
if (importData == null) importData = new ImportDataViewModel();
if (file == null)
return RedirectToAction("DataImport");
if (file.ContentLength > 0)
{
ImportDataFile idFile = new ImportDataFile { File = file };
importData.Files.Add(idFile);
}
SessionBag.Current.ImportData = importData;
return RedirectToAction("DataImport");
}
je suis pleinement conscient que, dans la plupart des cas, ce serait une mauvaise solution. Mais pour les quelques Ko de mémoire de serveur que les fichiers occupent et avec la simplicité de tout cela, je pense que cela a fonctionné très bien pour moi.
le bonus supplémentaire de l'utilisation du sac de session est que si l'Utilisateur a entré un élément de menu différent et est ensuite revenu, la liste des fichiers serait toujours là. Ce ne serait pas le cas, par exemple lors du choix des champs du formulaire/du stockage du fichier. option.
pour conclure, je me rends compte que le sac de session est très facile à abuser, étant donné la simplicité d'utilisation. Mais si vous l'utilisez pour ce qu'il est censé, à savoir les données de session, je pense qu'il peut être un outil puissant.
Concernant Le Téléchargement
1) peut-être considérer un uploader AJAX avec HTML pour permettre à votre utilisateur de sélectionner plusieurs fichiers avant ils sont envoyés au serveur. Ce uploader de fichiers BLUEIMP jQuery AJAX est assez étonnant avec une api assez grande:Blueimp JQuery File Upload. Il permettra aux utilisateurs de faire glisser et déposer ou de sélectionner plusieurs fichiers et modifier l'ordre des fichiers, inclure/exclure, etc.. Puis, quand ils sont heureux, ils peuvent appuyer sur télécharger pour envoyer à votre contrôleur ou gestionnaire de téléchargement pour le traitement du côté du serveur.
2) vous pourriez faire chaque téléchargement persister dans la base de données, bien que vous seriez recharger la page entière et l'écriture d'un certain modèle de vue supplémentaire et le code de rasoir pour atteindre l'effet de listing. Ce n'est probablement pas d'être sensible...
concernant la conservation des formulaires Web de L'État / MVC
maintenir l'état entre les requêtes est quelque peu de la magie noire et du vaudou. Quand on entre ASP.NET MVC, allez dans il comprendre que les applications web communiquer à l'aide de requêtes et les réponses. Alors allez dans embrassant le web comme apatride et de développer à partir de là! Quand votre model est posté par votre controller, c'est passé avec toutes les variables dans le contrôleur! Toutefois, avant qu'il ne disparaisse, vous pouvez stocker son contenu dans une base de données pour une récupération ultérieure.
les applications Web ne peuvent pas garder l'état vrai comme les applications de bureau. Il ya plusieurs façons Ajax cadres, et certains les outils voodoo que les gens utilisent pour simuler un État dans L'environnement HTTP. Et une simulation d'état n'est vraiment qu'une fausse imitation de l'état. ASP.NET Web Forms essaie de simuler l'état comme il peut en sorte de cacher la nature apatride de HTTP du développeur. Vous pouvez rencontrer beaucoup de maux de tête lorsque vous essayez d'employer votre propre code AJAX en tandem avec le code de balisage de formulaires Web et son propre cadre Ajax.
je suis vraiment content que vous appreniez MVC
toutes les blagues mises à part, si vous obtenez la mentalité MVC/HTTP/Statelessness, il sera très facile d'appliquer les modèles à D'autres cadres super populaires comme Ruby on Rails, SpringMVC (java), Django (python), CakePHP etc... Ce transfert facile de connaissances vous aidera à devenir un bien meilleur développeur et obtenir vraiment bon à Ajax.
je suis vraiment heureux que vous appreniez MVC 3, j'ai été avec quelques stages dans quelques très grandes entreprises qui avaient ces grands fous ASP.NET Web Les projets de formulaires avec le code volant partout juste pour changer quelques valeurs numériques dans la base de données (-_-') ont eu L'impression que j'utilisais des ciseaux pour tricoter la chaussette d'un bébé. Un faux mouvement facile, et tout est cassé. Il se sentait comme se développer en PHP, vous sortez suant et pas vraiment sûr de ce qui s'est passé et sur quelle ligne. Il était presque impossible de déboguer et de mettre à jour.