91 votes

Exclusion d'une propriété lors d'une mise à jour dans Entity Framework

J'ai cherché un moyen approprié de marquer une propriété pour qu'elle ne soit PAS modifiée lors de la mise à jour d'un modèle dans MVC.

Par exemple, prenons ce petit modèle :

class Model
{
    [Key]
    public Guid Id {get; set;}
    public Guid Token {get; set;}

    //... lots of properties here ...
}

alors la méthode d'édition que MVC crée ressemble à ceci :

[HttpPost]
public ActionResult Edit(Model model)
{
    if (ModelState.IsValid)
    {
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(model);
}

Maintenant, si ma vue ne contient pas le jeton, il sera annulé par cette modification.

Je cherche quelque chose comme ça :

db.Entry(model).State = EntityState.Modified;
db.Entry(model).Property(x => x.Token).State = PropertyState.Unmodified;
db.SaveChanges();

La meilleure solution que j'ai trouvée jusqu'à présent est d'être inclusif et de définir à la main toutes les propriétés que je veux inclure, mais je ne veux vraiment dire que celles qui doivent être exclues.

180voto

AndroidUser Points 434

Nous pouvons l'utiliser comme ceci

 db.Entry(model).State = EntityState.Modified;
 db.Entry(model).Property(x => x.Token).IsModified = false;
 db.SaveChanges();

il sera mis à jour mais sans la propriété Token

16voto

Mousa Alribi Points 1

Toute personne cherchant à savoir comment réaliser cela sur EF Core. C'est à peu près la même chose mais votre IsModified doit être après que vous ayez ajouté le modèle à mettre à jour.

db.Update(model);
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();

@svendk mis à jour :

Et si vous (comme moi) vous demandez pourquoi model n'ont pas le jeton avant ou après db.SaveChanges(), c'est parce qu'avec Update, l'entité n'est en fait pas récupérée - seule une clause SQL Update est envoyée - donc le contexte ne connaît pas votre model Les données préexistantes de l'entreprise ne sont pas prises en compte, seules les informations que vous lui avez fournies dans le formulaire de demande d'accès à l'information sont prises en compte. db.Update(mode) . Même si vous Find(model.id) vous n'obtenez pas la mise à jour de votre contexte, puisqu'il y a déjà chargé un model dans le contexte, il n'est toujours pas récupéré dans la base de données.

Si vous (comme moi) vouliez retourner le modèle fini tel qu'il apparaît dans la base de données, vous pouvez faire quelque chose comme ceci :

db.Update(model);
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();

// New: Reload AFTER savechanges, otherwise you'll forgot the updated values
db.Entry(model).Reload();

Maintenant model est chargé à partir de la base de données avec toutes les valeurs, les valeurs mises à jour et les (autres) valeurs préexistantes.

11voto

Barisa Puter Points 2742

Créez un nouveau modèle qui aura un ensemble limité de propriétés que vous voulez mettre à jour.

C'est-à-dire que si votre modèle d'entité est :

public class User
{
    public int Id {get;set;}
    public string Name {get;set;}
    public bool Enabled {get;set;}
}

Vous pouvez créer un modèle de vue personnalisé qui permettra à l'utilisateur de modifier le nom, mais pas l'indicateur Activé :

public class UserProfileModel
{
   public int Id {get;set;}
   public string Name {get;set;}
}

Lorsque vous voulez faire une mise à jour de la base de données, vous procédez comme suit :

YourUpdateMethod(UserProfileModel model)
{
    using(YourContext ctx = new YourContext())
    { 
        User user = new User { Id = model.Id } ;   /// stub model, only has Id
        ctx.Users.Attach(user); /// track your stub model
        ctx.Entry(user).CurrentValues.SetValues(model); /// reflection
        ctx.SaveChanges();
    }
}

Lorsque vous appelez cette méthode, vous mettez à jour le nom, mais la propriété Enabled reste inchangée. J'ai utilisé des modèles simples, mais je pense que vous comprendrez comment l'utiliser.

3voto

Ali Yousefie Points 987

J'ai créé un moyen facile de modifier les propriétés des entités que je vais partager avec vous. Ce code va éditer les propriétés Nom et Famille de l'entité :

    public void EditProfileInfo(ProfileInfo profileInfo)
    {
        using (var context = new TestContext())
        {
            context.EditEntity(profileInfo, TypeOfEditEntityProperty.Take, nameof(profileInfo.Name), nameof(profileInfo.Family));
        }
    }

Et ce code ignorera la modification des propriétés Nom et Famille de l'entité et modifiera d'autres propriétés :

    public void EditProfileInfo(ProfileInfo profileInfo)
    {
        using (var context = new TestContext())
        {
            context.EditEntity(profileInfo, TypeOfEditEntityProperty.Ignore, nameof(profileInfo.Name), nameof(profileInfo.Family));
        }
    }

Utilisez cette extension :

public static void EditEntity<TEntity>(this DbContext context, TEntity entity, TypeOfEditEntityProperty typeOfEditEntityProperty, params string[] properties)
   where TEntity : class
{
    var find = context.Set<TEntity>().Find(entity.GetType().GetProperty("Id").GetValue(entity, null));
    if (find == null)
        throw new Exception("id not found in database");
    if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Ignore)
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            if (properties.Contains(item.Name))
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    else if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Take)
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            if (!properties.Contains(item.Name))
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    else
    {
        foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
        {
            if (!item.CanRead || !item.CanWrite)
                continue;
            item.SetValue(find, item.GetValue(entity, null), null);
        }
    }
    context.SaveChanges();
}

public enum TypeOfEditEntityProperty
{
    Ignore,
    Take
}

1voto

Jaime Points 487

Je suppose que vous ne voulez pas que la propriété soit modifiée uniquement dans certains cas, car si vous n'allez jamais l'utiliser dans votre application, il suffit de la supprimer de votre modèle.

Si vous voulez l'utiliser uniquement dans certains scénarios et éviter son "annulation" dans le cas ci-dessus, vous pouvez essayer :

  • Masquez le paramètre dans la vue avec HiddenFor :

    @Html.HiddenFor(m => m.Token)

Ainsi, la valeur originale ne sera pas modifiée et sera transmise au contrôleur.

Chargez à nouveau votre objet dans le contrôleur à partir de votre DBSet et exécutez cette méthode. Vous pouvez spécifier à la fois une liste blanche et une liste noire de paramètres qui doivent ou ne doivent pas être mis à jour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X