web-dev-qa-db-fra.com

Exemple réel de TryUpdateModel, ASP .NET MVC 3

Je ne peux pas comprendre comment utiliser TryUpdateModel et enregistrer l'architecture MVC en même temps.

Si je ne me trompe pas, le travail avec les contextes de données doit être dans le modèle. Donc, un tel code

var db=new TestEverybody();//it is class, which was generated by EntityFramework 
var currentTesting=db.Testing.(t => t.id == id).First();

doit être situé dans le modèle, pas dans le contrôleur, non?

Mais les exemples habituels d'utilisation de TryUpdateModel sont les suivants:

    public ActionResult Edit(Testing obj)//Testing collection
    {
        var db = new TestEverybody();
        var currentTesting=db.Testing.(t => t.id == obj.id).First();
        TryUpdateModel(currentTesting);
        db.SaveChanges();            
        return RedirectToAction("Index");
    }

De cette façon, ne casse-t-il pas l'architecture MVC? Nous travaillons avec une base de données dans le contrôleur, pas dans la classe spéciale Model.

Alors, quelle est la meilleure façon d'utiliser TryUpdateModel dans un vrai projet?

29
Sir Hally

Depuis l'OP demandé, voici un exemple du modèle ViewModel, ou comme j'aime l'appeler - ASP.NET MVC fait correctement.

Alors pourquoi utiliser un modèle spécifique à la vue

  1. Vous devez uniquement transmettre les informations dont vous avez besoin à votre vue.
  2. Souvent, vous devrez ajouter des métadonnées de vue supplémentaires (telles que des attributs de titre/description). Ceux-ci n'appartiennent pas à vos entités.
  3. L'utilisation de TryUpdateModel/UpdateModel est incorrecte. Ne pas utiliser (je vais vous expliquer pourquoi).
  4. Il est très rare que vos modèles de vues correspondent exactement à vos entités. Les gens finissent souvent par ajouter des éléments supplémentaires à leurs entités ou (pas beaucoup mieux) simplement en utilisant ViewBag plutôt que des propriétés de modèle de vue fortement typées.
  5. Si vous utilisez un ORM, vous pouvez rencontrer des problèmes avec les propriétés chargées paresseux (N + 1). Vos vues ne doivent pas émettre de requêtes.

Nous allons commencer avec une simple entité:

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal Price {get;set;}
}

Et disons que vous avez un formulaire simple où l'utilisateur peut seulement mettre à jour le Name et Description du produit. Mais vous utilisez (le très gourmand) TryUpdateModel.

J'utilise donc un certain nombre d'outils (comme Fiddler) pour construire moi-même un POST et envoyer ce qui suit:

Nom = WhatverIWant & Description = UnluckyFool & Price = 0

Eh bien, le classeur de modèle ASP.NET MVC va inspecter la collection de formulaires en entrée, voir que ces propriétés existent sur votre entité et les lier automatiquement pour vous. Ainsi, lorsque vous appelez "TryUpdateModel" sur l'entité que vous venez de récupérer de votre base de données, toutes les propriétés correspondantes seront mises à jour (y compris le prix!). Il est temps pour une nouvelle option.

Voir le modèle spécifique

public class EditProductViewModel {
    [HiddenInput]
    public Guid Id {get;set;}

    [Required]
    [DisplayName("Product Name")]
    public string Name {get;set;}

    [AllowHtml]
    [DataType(DataType.MultilineText)]
    public string Description {get;set;}
}

Cela ne contient que les propriétés dont nous avons besoin à notre avis. Notez que nous avons également ajouté des attributs de validation, des attributs d'affichage et des attributs spécifiques mvc.

En n'étant pas limité dans ce que nous avons dans notre modèle de vue, cela peut rendre vos vues beaucoup plus propres. Par exemple, nous pourrions afficher l'intégralité de notre formulaire d'édition en ayant les éléments suivants à notre avis:

@Html.EditorFor(model => model)

Mvc inspectera tous les attributs que nous avons ajoutés à notre modèle de vue et câblera automatiquement la validation, les étiquettes et les champs de saisie corrects (c'est-à-dire une zone de texte pour la description).

AFFICHAGE DU FORMULAIRE

[HttpPost]
public ActionResult EditProduct(EditProductViewModel model) {

    var product = repository.GetById(model.Id);

    if (product == null) {
        return HttpNotFound();
    }

    // input validation
    if (ModelState.IsValid) {

        // map the properties we **actually** want to update
        product.Name = model.Name;
        product.Description = model.Description;

        repository.Save(product);

        return RedirectToAction("index");
    }

    return View(model)
}

Il est assez évident d'après ce code ce qu'il fait. Nous n'avons aucun effet indésirable lorsque nous mettons à jour notre entité, car nous définissons explicitement des propriétés sur notre entité.

J'espère que cela explique suffisamment le modèle View-Model pour que vous souhaitiez l'utiliser.

114
Ben Foster

Donc, ce code doit être situé dans le modèle, pas dans le contrôleur, n'est-ce pas?

Pas nécessairement. Personnellement, je préfère mettre le code d'accès aux données dans un référentiel. Utilisez ensuite l'injection de constructeur pour transmettre une implémentation de référentiel spécifique au contrôleur (par exemple, si j'utilisais EF, j'écrirais une implémentation de référentiel EF). Ainsi, le contrôleur ressemblera à ceci:

public class HomeController: Controller
{
    private readonly IMyRepository _repository;
    public HomeController(IMyRepository repository)
    {
        _repository = repository;
    }

    public ActionResult Edit(int id)
    {
        var currentTesting = _repository.GetTesting(id);
        TryUpdateModel(currentTesting);
        _repository.SaveChanges();            
        return RedirectToAction("Index");
    }
}
19
Darin Dimitrov