Question Entity Framework 5 Mise à jour d'un enregistrement


J'ai exploré différentes méthodes d'édition / mise à jour d'un enregistrement dans Entity Framework 5 dans un environnement ASP.NET MVC3, mais jusqu'à présent aucun d'entre eux ne cochera toutes les cases dont j'ai besoin. Je vais expliquer pourquoi.

J'ai trouvé trois méthodes auxquelles je vais mentionner les avantages et les inconvénients:

Méthode 1 - Charger l'enregistrement d'origine, mettre à jour chaque propriété

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Avantages

  • Peut spécifier quelles propriétés changent
  • Les vues n'ont pas besoin de contenir toutes les propriétés

Les inconvénients

  • 2 requêtes sur la base de données pour charger l'original puis le mettre à jour

Méthode 2 - Charger l'enregistrement d'origine, définir les valeurs modifiées

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Avantages

  • Seules les propriétés modifiées sont envoyées à la base de données

Les inconvénients

  • Les vues doivent contenir toutes les propriétés
  • 2 requêtes sur la base de données pour charger l'original puis le mettre à jour

Méthode 3 - Joindre un enregistrement mis à jour et définir l'état à EntityState.Modified

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Avantages

  • 1 requête sur la base de données à mettre à jour

Les inconvénients

  • Impossible de spécifier les propriétés qui changent
  • Les vues doivent contenir toutes les propriétés

Question

Ma question à vous les gars; Y at-il une manière propre que je puisse atteindre cet ensemble d'objectifs?

  • Peut spécifier quelles propriétés changent
  • Les vues n'ont pas besoin de contenir toutes les propriétés (comme le mot de passe!)
  • 1 requête sur la base de données à mettre à jour

Je comprends que c'est une chose assez mineure à signaler, mais il me manque peut-être une solution simple à cela. Si non la méthode un prévaudra ;-)


811
2018-03-11 10:27


origine


Réponses:


Tu recherches:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();

645
2018-03-11 13:12



J'aime vraiment la réponse acceptée. Je crois qu'il y a encore une autre façon d'aborder cela. Imaginons que vous ayez une très courte liste de propriétés que vous ne voudriez jamais inclure dans une vue, donc lors de la mise à jour de l'entité, celles-ci seraient omises. Disons que ces deux champs sont Mot de passe et SSN.

db.Users.Attach(updatedUser);

var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;

entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;   

db.SaveChanges();   

Cet exemple vous permet essentiellement de laisser votre logique métier après avoir ajouté un nouveau champ à votre table Utilisateurs et à votre vue.


168
2017-08-01 21:01



foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
    if (propertyInfo.GetValue(updatedUser, null) == null)
        propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();

27
2017-11-12 11:33



J'ai ajouté une méthode de mise à jour supplémentaire à ma classe de base de référentiel similaire à la méthode de mise à jour générée par Scaffolding. Au lieu de définir l'objet entier sur "modifié", il définit un ensemble de propriétés individuelles. (T est un paramètre générique de classe.)

public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate)
{
    Context.Set<T>().Attach(obj);

    foreach (var p in propertiesToUpdate)
    {
        Context.Entry(obj).Property(p).IsModified = true;
    }
}

Et puis appeler, par exemple:

public void UpdatePasswordAndEmail(long userId, string password, string email)
{
    var user = new User {UserId = userId, Password = password, Email = email};

    Update(user, u => u.Password, u => u.Email);

    Save();
}

J'aime un voyage dans la base de données. Il est probablement préférable de le faire avec des modèles de vue, afin d'éviter de répéter des ensembles de propriétés. Je ne l'ai pas encore fait parce que je ne sais pas comment éviter d'apporter les messages de validation sur mon validateur de modèle de vue dans mon projet de domaine.


21
2018-04-25 16:19



public interface IRepository
{
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}

public class Repository : DbContext, IRepository
{
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
    {
        Set<T>().Attach(obj);
        propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
        SaveChanges();
    }
}

7
2018-03-16 16:04



Juste pour ajouter à la liste des options. Vous pouvez également récupérer l'objet de la base de données et utiliser un outil de mappage automatique comme Auto Mapper  pour mettre à jour les parties de l'enregistrement que vous voulez changer ..


2
2017-12-03 15:58



Selon votre cas d'utilisation, toutes les solutions ci-dessus s'appliquent. C'est comme ça que je le fais habituellement:

Pour le code côté serveur (par exemple, un traitement par lots), je charge généralement les entités et travaille avec des proxies dynamiques. Habituellement, dans les processus par lots, vous devez charger les données de toute façon au moment de l'exécution du service. J'essaie de charger par lots les données au lieu d'utiliser la méthode find pour gagner du temps. En fonction du processus, j'utilise un contrôle de concurrence optimiste ou pessimiste (j'utilise toujours optimiste sauf pour les scénarios d'exécution parallèles où j'ai besoin de verrouiller certains enregistrements avec des instructions sql simples, ce qui est rare). Selon le code et le scénario, l'impact peut être réduit à presque zéro.

Pour les scénarios côté client, vous avez quelques options

  1. Utilisez les modèles de vue. Les modèles doivent avoir une propriété UpdateStatus (unmodified-inserted-updated-deleted). Il est de la responsabilité du client de définir la valeur correcte pour cette colonne en fonction des actions de l'utilisateur (insertion-mise à jour-suppression). Le serveur peut interroger la base de données pour les valeurs d'origine ou le client doit envoyer les valeurs d'origine au serveur avec les lignes modifiées. Le serveur doit attacher les valeurs d'origine et utiliser la colonne UpdateStatus pour chaque ligne pour décider comment gérer les nouvelles valeurs. Dans ce scénario, j'utilise toujours une concurrence optimiste. Cela ne fera que les instructions insert - update - delete et non les sélections, mais il faudra peut-être du code intelligent pour parcourir le graphique et mettre à jour les entités (cela dépend de votre scénario - application). Un mappeur peut aider mais ne gère pas la logique CRUD

  2. Utilisez une bibliothèque comme breeze.js qui cache la majeure partie de cette complexité (comme décrit en 1) et essayez de l'adapter à votre cas d'utilisation.

J'espère que cela aide


1
2018-05-30 12:07