web-dev-qa-db-fra.com

Entity Framework 5 Mise à jour d'un enregistrement

J'explorais différentes méthodes de modification/mise à jour d'un enregistrement dans Entity Framework 5 dans un environnement ASP.NET MVC3, mais aucune d'entre elles ne coche toutes les cases dont j'ai besoin. Je vais expliquer pourquoi.

J'ai trouvé trois méthodes pour lesquelles 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();
}    

Pros

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

Inconvénients

  • 2 x 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();
}

Pros

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

Inconvénients

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

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

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

Pros

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

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 un moyen propre que je puisse atteindre cet ensemble d'objectifs?

  • Peut spécifier quelles propriétés changent
  • Les vues ne doivent pas nécessairement contenir toutes les propriétés (telles que le mot de passe!)
  • 1 x requête sur la base de données à mettre à jour

Je comprends que c’est une chose mineure à souligner, mais il me manque peut-être une solution simple à cela. Sinon méthode on prévaudra ;-)

847
Stokedout

Tu recherches:

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

J'aime beaucoup la réponse acceptée. Je crois qu’il ya encore une autre façon d’aborder cela. Supposons que vous disposiez d'une très courte liste de propriétés que vous ne voudriez jamais inclure dans une vue. Par conséquent, lors de la mise à jour de l'entité, celles-ci seraient omises. Disons que ces deux champs sont Password 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 de laisser essentiellement votre logique métier après avoir ajouté un nouveau champ à votre table Utilisateurs et à votre vue.

174
smd
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();
28
Stefano Camisassi

J'ai ajouté une méthode de mise à jour supplémentaire à la classe de base de mon référentiel similaire à la méthode de mise à jour générée par Scaffolding. Au lieu de définir l'objet modifié 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 à la base de données. Il est probablement préférable de le faire avec les modèles de vue, afin d'éviter de répéter des ensembles de propriétés. Je ne l'ai pas encore fait car je ne sais pas comment éviter d'inclure les messages de validation de mes validateurs de modèle de vue dans mon projet de domaine.

22
Ian Warburton
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();
    }
}
11

Juste pour ajouter à la liste des options. Vous pouvez également extraire l'objet de la base de données et utiliser un outil de mappage automatique tel que Auto Mapper pour mettre à jour les parties de l'enregistrement que vous souhaitez modifier.

4
Bostwick

Selon votre cas d'utilisation, toutes les solutions ci-dessus s'appliquent. Voici comment 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 mandataires dynamiques. Habituellement, dans les processus par lots, vous devez quand même charger les données 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 d'accès concurrentiel optimiste ou pessimiste (j'utilise toujours l'optimisme, sauf dans le cas de scénarios d'exécution parallèle dans lesquels j'ai besoin de verrouiller certains enregistrements avec des instructions SQL simples, mais c'est rare). En fonction du code et du 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 (non modifiée, insérée, mise à jour, supprimée). Il incombe au client de définir la valeur correcte sur cette colonne en fonction des actions de l'utilisateur (insert-update-delete). Le serveur peut interroger la base de données pour connaître les valeurs d'origine ou le client doit envoyer les valeurs d'origine au serveur avec les lignes modifiées. Le serveur doit associer les valeurs d'origine et utiliser la colonne UpdateStatus pour chaque ligne afin de décider de la gestion des nouvelles valeurs. Dans ce scénario, j'utilise toujours une concurrence optimiste. Cela ne fera que les instructions insert - update - delete et pas les sélections, mais il faudra peut-être un code intelligent pour parcourir le graphique et mettre à jour les entités (selon votre scénario - application). Un mappeur peut aider mais ne gère pas la logique CRUD

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

J'espère que ça aide

3
Chriss