2009-04-29 13 views
3

j'ai le scénario suivant:Modification des entités dans le EntityFramework

  1. Les entités sont chargées à partir de la base de données.
  2. L'un d'eux est présenté à l'utilisateur dans un formulaire (WPF UserControl) dans lequel l'utilisateur peut modifier les propriétés de cette entité.
  3. L'utilisateur peut décider d'appliquer les modifications à l'entité ou d'annuler l'édition.

Comment est-ce que j'implémenterais quelque chose comme ceci avec le EntityFramework?

Mon problème est que, lorsque je lie directement l'interface utilisateur aux propriétés de l'entité, chaque modification est instantanément appliquée à l'entité. Je veux retarder cela au moment où l'utilisateur appuie sur OK et l'entité est validée avec succès.

J'ai pensé à charger les entités avec NoTracking et en appelant ApplyPropertyChanges après que l'entité détachée a été validée, mais je ne suis pas entièrement sûr de la bonne façon de le faire. Le document de l'EntityFramework à MSDN est très clairsemé.

Une autre façon que je pourrais penser est à Refresh l'entité avec StoreWins, mais je n'aime pas réinitialiser les changements à Annuler au lieu d'appliquer des changements à Ok.

Quelqu'un at-il un bon tutoriel ou un échantillon?

Répondre

3

Une option est ce que vous avez dit faire une requête sans suivi.

ctx.Customers.MergeOption = MergeOption.NoTracking; 
var customer = ctx.Customers.First(c => c.ID == 232); 

Ensuite, le client peut modifier 'customer' selon les besoins en mémoire, et rien ne se passe réellement dans le contexte.

Maintenant, quand vous voulez réellement faire le changement que vous pouvez faire:

// get the value from the database 
var original = ctx.Customers.First(c => c.ID == customer.ID); 
// copy values from the changed entity onto the original. 
ctx.ApplyPropertyChanges(customer); . 
ctx.SaveChanges(); 

Maintenant, si vous êtes mal à l'aise avec la requête soit pour des raisons de performance ou la concurrence, vous pouvez ajouter une nouvelle méthode d'extension AttachAsModified (.. .) à ObjectContext.

qui ressemble à ceci:

public static void AttachAsModified<T>(
    this ObjectContext ctx, 
    string entitySet, 
    T entity) 
{ 
    ctx.AttachTo(entitySet, entity); 

    ObjectStateEntry entry = 
      ctx.ObjectStateManager.GetObjectStateEntry(entity); 

    // get all the property names 
    var propertyNames = 
      from s in entry.CurrentValues.DataRecordInfo.FieldMetadata 
      select s.FieldType.Name; 

    // mark every property as modified  
    foreach(var propertyName in propertyNames) 
    { 
     entry.SetModifiedProperty(propertyName); 
    } 
} 

Maintenant, vous pouvez écrire du code comme ceci:

ctx.Customers.MergeOption = MergeOption.NoTracking; 
var customer = ctx.Customers.First(); 
// make changes to the customer in the form 
ctx.AttachAsModified("Customers", customer); 
ctx.SaveChanges(); 

Et maintenant, vous avez pas ou requêtes extranous concurrency.

Le seul problème concerne maintenant les propriétés FK. Vous devriez probablement à mon index de conseils pour aider ici: http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx

Hope this helps

Alex

+0

Je l'ai actuellement mis en œuvre avec l'approche ApplyPropertyChanges, mais la fixation d'une entité modifiée semble également intéressante. Merci pour l'entrée! –

1

La méthode normale consiste à lier à quelque chose qui implémente IEditableObject. Si et comment cela cadre avec le cadre de l'entité, je ne suis pas sûr.

1

Je suggère aussi IEditableObject et IDataErrorInfo. La façon dont je le fais est, j'ai fondamentalement un modèle de vue pour une entité qui prend l'entité en tant que paramètre de constructeur (fondamentalement un objet de wrapper).

Dans BeginEdit, je copie les propriétés de l'entité à mon viewmodel, donc si je fais CancelEdit, les données sont seulement changées dans le ViewModel et l'Entité d'origine n'a pas changé. Dans EndEdit, j'applique à nouveau les propriétés ViewModel à l'entité, ou bien seulement si la validation a réussi.

Pour la validation, j'utilise les méthodes de IDataErrorInfo. J'implémente simplement IDataErrorInfo.Error afin qu'il vérifie chaque nom de propriété via IDataErrorInfo [string columnName] et concatène des messages d'erreur éventuels. Si c'est vide, tout va bien. (Je ne sais pas si Error est censé être utilisé de cette façon, mais je le fais)

Si d'autres entités sont attachées à mon entité d'origine, par exemple Customer.Orders, je les crée en tant que ViewModels imbriqués dans ViewModel de l'entité d'origine. . Le ViewModel original appelle ses méthodes Begin, Cancel, EndEdit/Error de subModels dans ses propres implémentations de ces méthodes.

C'est un peu plus de travail, mais je pense que ça vaut le coup car entre BeginEdit et EndEdit, vous pouvez être sûr que rien ne change sans que vous le remarquiez. Et avoir un extrait de code pour les propriétés INotifyPropertyChanged-enabled aide beaucoup aussi.

Questions connexes