2017-10-18 3 views
0

Je souhaite mettre à jour une table de base de données. Le contrôleur reçoit un modèle de vue avec les données appropriées. Si j'attribuer à chaque variable membre comme dans l'exemple ci-dessous cela fonctionneAffecter des variables de membre du modèle de vue au résultat de la requête

[HttpPost] 
public ActionResult Edit(Admin_vm vm) { 
    var result = (from Users in db.Users 
           where Users.ID == usrID 
           select Users 
       ).FirstOrDefault(); 

    result.CompanyName = vm.ModifyUser.CompanyName; 
    //this is where I would assign every single member variable 
    db.SaveChanges(); 
    return View(vm); 
} 

Cependant, je me demande s'il y a un moyen d'affecter directement l'objet modèle de vue comme ceci:

result = vm.ModifyUser; 

Cela ne donne pas moi une erreur, mais il n'attribue pas les variables membres. Y a-t-il un moyen facile de faire ceci?

Merci!

+0

Les ViewModels sont utilisés pour valider les entrées de l'utilisateur. Ils devraient être traités, validés puis convertis en entités. Je voudrais également utiliser db.Users.FirstOrDefaultAsync (x => x.Id == userID). Si cette valeur est nulle, cela signifie que votre utilisateur ne quitte pas. Dans ce cas, votre code va planter. – Seb

+0

Vous voulez dire que je ne devrais pas passer le modèle de vue au contrôleur? Désolé, je ne comprends pas votre point. – mneumann

+0

Vous devez passer la VM de la vue au contrôleur. Vous ne devriez pas le passer du contrôleur au modèle de données :) En théorie, votre machine virtuelle et votre modèle peuvent être différents, donc cela ne fonctionnerait pas. J'espère que je suis clair! – Seb

Répondre

1

Vous ne pouvez pas définir directement votre objet de modèle de vue sur votre objet d'entité de base de données car ce sont des objets de classe différente. Au lieu de cela, vous pouvez mapper chaque propriété, cela peut être fait manuellement (comme vous le faites maintenant), en utilisant la réflexion ou en utilisant des bibliothèques existantes telles que ValueInjecter ou AutoMapper. Si les deux objets sont très similaires et ont les mêmes noms de propriété, j'utiliserais ValueInjecter car il est très rapide et simple à utiliser, AutoMapper est beaucoup plus lent mais permet des mappages plus complexes (j'ai eu de sérieux problèmes de performance avec mais choisissez celui qui convient le mieux à votre scénario, c'est-à-dire la vitesse par rapport à la flexibilité).

Voici comment le faire avec ValueInjecter pour l'exemple dans votre question:

Ajouter le ValueInjecter NuGet:

Install-Package ValueInjecter -Version 3.1.1.5 

Inclure l'espace de noms dans votre classe contrôleur:

using Omu.ValueInjecter; 

Et puis dans votre action:

[HttpPost] 
public ActionResult Edit(Admin_vm vm) { 
    var result = (from Users in db.Users 
           where Users.ID == usrID 
           select Users 
       ).FirstOrDefault(); 

    result.InjectFrom(vm.ModifyUser); 

    context.SaveChanges(); 

    return View(vm); 
} 

Pour la méthode Create vous devez créer votre premier objet:

User user = new User(); 
user.InjectFrom(vm.ModifyUser); 
+0

Ce n'est pas nécessairement vrai. Il ne peut pas utiliser le modèle avec le contexte, mais il peut utiliser l'entité avec la vue (en tant que modèle). Si l'entité est liée de manière appropriée par le classeur modèle, vous pouvez toujours passer l'entité d'avant en arrière et la rattacher au contexte et définir son état sur modifié, puis enregistrer les modifications. Je ne le recommande pas, mais c'est faisable. – JuanR

+1

Hola Juan. Oui, je suis d'accord que c'est faisable mais je ne le recommande pas car il serait plus difficile à maintenir quand l'entité de base de données change, vous pouvez également envoyer des propriétés qui ne sont pas affichées mais qui peuvent être interceptées . En outre, vous pouvez renvoyer des données inutiles qui augmenteront inutilement la bande passante. – Isma

2

Une solution possible consiste à utiliser AutoMapper qui mappera automatiquement les propriétés d'un objet à un autre tant que les propriétés sont nommées et typées de la même manière. Ainsi, vous pourrez peut-être faire quelque chose comme cela en fonction des noms de propriété dans votre classe (vous pouvez également définir des cartes personnalisées si vos propriétés sont nommées differnetly):

//create the map, this is normally done in a config file but can be done in many different places 
Mapper.Initialize(x => 
{ 
     x.CreateMap<User, type of vm.ModifyUser goes here>().ReverseMap(); 
} 

//map the vm to the user, this will update all fields on result to what is contained in vm.ModifyUser assuming properties are named and typed the same. 
....db query to retrieve result... 
result = Mapper.Map<User>(vm.ModifyUser); 
context.SaveChanges() 

vous pouvez ajouter AutoMapper via le package NuGet Gestionnaire . C'est un outil largement utilisé qui est bien documenté.

0

Il semble que le modèle de liaison ne fonctionne pas comme prévu. Vous devrez peut-être écrire un classeur de modèle personnalisé. L'exemple ci-dessous a été tiré de here. Cet exemple hérite de DefaultModelBinder et utilise ensuite la réflexion pour SEULEMENT Remplacer les fixations quand il est du type spécifié (HomePageModels dans l'exemple ci-dessous, mais serait vos objets ViewModel ViewModel ou spécifique - c.-à-Admin_vm ou ModifyUser) . Pour tout le reste, il se lie normalement, ce qui est probablement préférable.

public class HomeCustomDataBinder : DefaultModelBinder 
    { 

     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      if (bindingContext.ModelType == typeof(HomePageModels)) 
      { 
       HttpRequestBase request = controllerContext.HttpContext.Request; 

       string title = request.Form.Get("Title"); 
       string day = request.Form.Get("Day"); 
       string month = request.Form.Get("Month"); 
       string year = request.Form.Get("Year"); 

       return new HomePageModels 
       { 
        Title = title, 
        Date = day + "/" + month + "/" + year 
       }; 

       //// call the default model binder this new binding context 
       //return base.BindModel(controllerContext, newBindingContext); 
      } 
      else 
      { 
       return base.BindModel(controllerContext, bindingContext); 
      } 
     } 

    }