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
Inconvénients
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
Inconvénients
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
Inconvénients
Question
Ma question à vous les gars; Y at-il un moyen propre que je puisse atteindre cet ensemble d'objectifs?
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 ;-)
Tu recherches:
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
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.
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();
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.
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();
}
}
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.
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
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
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