web-dev-qa-db-fra.com

ASP.Net MVC et état - comment conserver l'état entre les demandes

En tant que développeur ASP.Net assez expérimenté qui a récemment commencé à utiliser MVC, je me retrouve un peu à changer mon état d'esprit d'une manière traditionnelle de "contrôler le serveur et le gestionnaire d'événements", en une manière plus dynamique de MVC. Je pense que j'y arrive lentement, mais parfois la "magie" MVC me décourage.

Mon scénario actuel consiste à 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 une liste de fichiers avec lesquels travailler. Lorsqu'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 de créer une liste de fichiers et de conserver cette liste entre les demandes. Dans l'approche traditionnelle, cela serait extrêmement simple - les données seraient conservées dans ViewState. Mais dans MVC, je dois transmettre les données entre le contrôleur et les vues, et je ne comprends pas complètement comment cela est censé fonctionner.

Je suppose que je ferais mieux de poster ma tentative plutôt incomplète de coder cela pour expliquer le problème.

Afin de conserver mes données de liste de fichiers, j'ai créé un modèle de vue qui est essentiellement une liste typé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>

La vue utilise le modèle de vue comme modèle:

@model MHP.ViewModels.ImportDataViewModel

Cela enverra 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 DataImport 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 du modèle de vue à l'intérieur de l'action, puis aussi si je mets un point d'arrêt à l'intérieur de la vue et que je débogue "this.Model", tout est bien.

Mais ensuite, si j'essaie de télécharger un autre fichier, lorsque je place un point d'arrêt dans l'action AddImportFile, le paramètre importData est vide. Il est donc évident que la vue ne transmet pas l'instance actuelle de son modèle à l'action.

Dans les exemples MVC que j'ai consultés, l'instance de modèle est passée "comme par magie" à la méthode d'action en tant que paramètre, alors pourquoi est-elle vide maintenant?

Je suppose que le vrai problème est ma compréhension limitée de MVC et qu'il existe probablement une solution très simple à cela. Quoi qu'il en soit, je serais extrêmement reconnaissant si quelqu'un pouvait m'orienter dans la bonne direction.

51
TMan

Cela fait un moment que je n'ai pas posté la question, qui était assez colorée par ma petite expérience et ma connaissance de MVC. J'ai quand même reçu des informations très utiles, qui m'ont finalement amené à trouver une solution et à mieux comprendre MVC.

Ce qui m'a dérouté en premier lieu, c'est que vous pouviez avoir un contrôleur avec un objet fortement typé comme paramètre, comme ceci:

public ActionResult DoSomething(MyClass myObject)...

Cet objet provenait du même contrôleur:

...
return View(myObject);
...

Cela m'a amené à croire que l'objet a vécu tout au long de ces deux étapes, et que je pouvais en quelque sorte m'attendre à ce que vous puissiez l'envoyer à la vue, faire quelque chose et ensuite "magiquement" le renvoyer au contrôleur.

Après avoir lu sur la liaison de modèle, j'ai compris que ce n'était bien sûr pas le cas. La vue est complètement morte et statique, et à moins que vous ne stockiez les informations quelque part, elles disparaissent.

Pour en revenir au problème, qui consistait à sélectionner et à télécharger des fichiers à partir du client, et à créer une liste de ces fichiers à afficher, j'ai réalisé qu'en général, il existe trois façons de stocker des informations entre les demandes dans MVC:

  1. Vous pouvez stocker des informations dans des champs de formulaire de la vue et les renvoyer ultérieurement au contrôleur
  2. Vous pouvez le conserver dans une sorte de stockage, par exemple un fichier ou une base de données
  3. Vous pouvez le stocker dans la mémoire du serveur en accédant à des objets qui vivent tout au long des requêtes, par exemple 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

L'approche "par le livre" serait probablement de stocker les métadonnées dans les champs du formulaire et le contenu du fichier dans un fichier ou dans db. Mais il y a aussi une autre façon. Comme je sais que mes fichiers sont assez petits et qu'il n'y en aura que quelques-uns, et que cette solution ne sera jamais déployée dans une batterie 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 supprimés, donc je ne voulais pas les stocker dans ma base de données.

Après avoir lu cet excellent article: Accès aux données de session ASP.NET à l'aide de Dynamics

J'étais convaincu. J'ai simplement créé une classe sessionbag comme décrit dans l'article, puis je pouvais faire ce qui suit dans mon contrôleur:

    [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 du serveur que les fichiers occupent et avec la simplicité de tout cela, je pense que cela a très bien fonctionné pour moi.

Le bonus supplémentaire de l'utilisation du SessionBag est que si l'utilisateur entrait dans un élément de menu différent puis revenait, la liste des fichiers serait toujours là. Ce ne serait pas le cas par exemple lors du choix de l'option champs de formulaire/stockage de fichiers.

Comme dernière remarque, je me rends compte que le SessionBag est très facile à abuser, étant donné la simplicité d'utilisation. Mais si vous l'utilisez pour ce à quoi il est destiné, à savoir les données de session, je pense que cela peut être un outil puissant.

48
TMan

Concernant le téléchargement

1) Peut-être envisagez un AJAX uploader avec HTML pour permettre à votre utilisateur de sélectionner plusieurs fichiers avant ils sont envoyés au serveur. Ce BlueImp Jquery AJAX uploader de fichier est assez étonnant avec une très bonne api: Blueimp Jquery File Upload . Il permettra aux utilisateurs de glisser-déposer ou de sélectionner plusieurs fichiers et modifier l'ordre des fichiers, inclure/exclure etc. Puis, quand ils sont satisfaits, ils peuvent appuyer sur upload pour les envoyer à votre contrôleur ou uploader le gestionnaire pour traitement côté serveur.

2) Vous pouvez faire en sorte que chaque téléchargement persiste dans la base de données, bien que vous rechargiez la page entière et écriviez un modèle de vue supplémentaire et un code de rasoir pour obtenir l'effet de liste. Cela ne va probablement pas être réactif ...

Concernant la conservation de l'état des formulaires Web/MVC

Garder l'état entre les demandes est quelque peu de la magie noire et du vaudou. Lorsque vous accédez à ASP.NET MVC, allez-y en comprenant que les applications Web communiquent à l'aide de requêtes et de réponses. Alors, adoptez le Web comme apatride et développez-vous à partir de là! Lorsque votre modèle est publié via votre contrôleur, il est disparu avec toutes les variables du contrôleur! Cependant, 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 conserver un état réel comme les applications de bureau. Il existe de nombreuses façons de créer des frameworks ajax et certains outils vaudou que les gens utilisent pour simuler l'état dans l'environnement HTTP. Et une simulation d'état n'est vraiment qu'un faux mimétisme de l'état. ASP.NET Web Forms essaie de simuler l'état du mieux qu'il peut en masquant en quelque sorte la nature sans état de HTTP au développeur. Vous pouvez rencontrer beaucoup de maux de tête lorsque vous essayez d'utiliser votre propre code AJAX en tandem avec le code de balisage Web Forms et son propre framework Ajax.

Je suis vraiment content que vous appreniez MVC

Toutes blagues à part, si vous obtenez la mentalité MVC/HTTP/Apatridie, il sera très facile d'appliquer les modèles à d'autres frameworks 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 à devenir VRAIMENT bon à Ajax.

Je suis vraiment content que vous appreniez MVC 3, j'ai fait quelques stages dans de très grandes entreprises qui avaient ces grands projets de formulaires Web ASP.NET fous avec du code volant partout juste pour changer quelques valeurs numériques dans la base de données (-_- ') Je me sentais comme si j'utilisais des ciseaux pour tricoter la chaussette d'un bébé. Un mauvais mouvement facile et tout s'est cassé. On avait l'impression de se développer en PHP, on sort de la transpiration et on ne sait pas trop ce qui s'est passé et sur quelle ligne. Il était presque impossible de déboguer et de mettre à jour.

11
Max Alexander