0

Je pense que c'est une question assez simple mais je suis toujours incapable de trouver une meilleure solution pour cela. Donc, après avoir fait des recherches sur ce sujet, j'ai pensé à poser cette question ici pour avoir une opinion d'expert.Comment faire pour activer ObservableCollection en tant que collection wrapper pour suivre les modifications?

Fondamentalement, je travaille sur l'application WPF et j'ai défini GenericObserableCollection<T> implémentant ObservableCollection<T> et la plupart des collections l'implémentent pour avoir une approche standard dans tout le projet.

[Serializable] 
[CollectionDataContract] 
public class GenericObservableCollection<T> : ObservableCollection<T> 
{ 
     public GenericObservableCollection() { } 
     public GenericObservableCollection(IEnumerable<T> collection) 
      : base(collection) { } 
} 

[Serializable] 
[CollectionDataContract] 
public class GenericRuleCollection : GenericObservableCollection<IRule> 
{ 
     public GenericRuleCollection() { } 
     public GenericRuleCollection(IEnumerable<IRule> collection) 
      : base(collection) { } 
} 

Dans un premier temps, il était bon, mais plus tard, quand Entity Framework est entré en l'image que je devais changer la conception de domaine de façon drastique, car EF nécessite d'exposer ICollection<T> pour la cartographie. À ce moment-là, j'étais confus en gardant le minimum de changements et je m'adaptais à EF parce que j'étais nouveau. Plus tard, après avoir fait des recherches, je suis tombé sur quelques bons articles traitant ce scénario.

I a appliqué la même approche dans mon domaine d'application créant ChildrenStorage pour tenir ICollection<GenericRule> comme une exigence pour EF.

Maintenant, je suis à la recherche d'une approche intelligente et élégante pour garder mes deux collections, c'est-à-dire ChildrenStorage et Children en synchronisation lorsque des éléments sont ajoutés et/ou supprimés. Puisque la collection Children est celle qui sera modifiée via l'interface utilisateur, je veux garder une trace de toutes les modifications apportées à Children et souhaite synchroniser ChildrenStorage.

[Serializable] 
[DataContract] 
public abstract class GenericContainerRule : GenericRule 
{ 
    protected GenericContainerRule() : this(null) 
    { 
     ChildrenStorage = new List<GenericRule>(); 
    } 

    protected GenericContainerRule(string name) : base(name) 
    { 
     ChildrenStorage = new List<GenericRule>(); 
    } 

    public void AddChild(IRule rule) 
    { 
     ChildrenStorage.Add(rule as GenericRule); 
     _children = new GenericRuleCollection(ChildrenStorage); 
     OnPropertyChanged(nameof(Children)); 
    } 

    public class OrMappings 
    { 
     public static Expression<Func<GenericContainerRule, ICollection<GenericRule>>> ChildrenAccessor = t => t.ChildrenStorage; 
    } 

    [DataMember] 
    protected ICollection<GenericRule> ChildrenStorage { get; set; } 
    private GenericRuleCollection _children; 
    public GenericRuleCollection Children => _children ?? (_children = new GenericRuleCollection(ChildrenStorage)); 

    private GenericRuleCollection _children; 
    [DataMember] 
    public virtual GenericRuleCollection Children 
    { 
     get { return _children; } 
     private set { SetProperty(ref _children, value); } 
    } 
} 
+1

Mais votre GenericObservableCollection implémente déjà ICollection , pourquoi vous avez besoin d'avoir une autre collection? – Evk

+0

Je n'ai pas besoin d'une autre collection, je veux juste que toutes les modifications de 'ChildrenStorage' soient reflétées automatiquement dans' Children' –

+1

Idéalement, vous ne devriez pas mélanger votre modèle de données EntityFramework avec votre viewmodel (ou même votre "vrai" modèle dépendant de loin vous voulez séparer les choses). Le modèle de données est vraiment destiné uniquement à l'accès aux données de la base de données, et ne sera pas utilisé lors de l'interaction avec des choses comme les règles métier. Vous utiliserez généralement une forme de mappeur automatique pour déplacer des données entre le modèle EF et votre modèle d'entreprise. –

Répondre

0

Je ne comprends pas que je pense bien ce que vous demandez, mais vous parlez de « changer les collections de suivi » comme un emballage de collection, celui-ci peut vous convenir. Je l'utilise actuellement pour mon projet de diplôme final. Je mets un lien à la fin.

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 
using System.ComponentModel; 
using System.Linq; 

namespace Core 
{ 
    public interface IValidatableTrackingObject : 
     IRevertibleChangeTracking, 
     INotifyPropertyChanged 
    { 
     bool IsValid { get; } 
    } 

    public class ChangeTrackingCollection<T> : ObservableCollection<T>, IValidatableTrackingObject 
     where T : class, IValidatableTrackingObject 
    { 
     private IList<T> _originalCollection; 

     private ObservableCollection<T> _addedItems; 
     private ObservableCollection<T> _removedItems; 
     private ObservableCollection<T> _modifiedItems; 

     public ChangeTrackingCollection(IEnumerable<T> items) 
      : base(items) 
     { 
      _originalCollection = this.ToList(); 

      AttachItemPropertyChangedHandler(_originalCollection); 

      _addedItems = new ObservableCollection<T>(); 
      _removedItems = new ObservableCollection<T>(); 
      _modifiedItems = new ObservableCollection<T>(); 

      this.AddedItems = new ReadOnlyObservableCollection<T>(_addedItems); 
      this.RemovedItems = new ReadOnlyObservableCollection<T>(_removedItems); 
      this.ModifiedItems = new ReadOnlyObservableCollection<T>(_modifiedItems); 
     } 

     public ReadOnlyObservableCollection<T> AddedItems { get; private set; } 
     public ReadOnlyObservableCollection<T> RemovedItems { get; private set; } 
     public ReadOnlyObservableCollection<T> ModifiedItems { get; private set; } 

     public bool IsChanged => AddedItems.Count > 0 
           || RemovedItems.Count > 0 
           || ModifiedItems.Count > 0; 

     public bool IsValid => this.All(t => t.IsValid); 

     public void AcceptChanges() 
     { 
      _addedItems.Clear(); 
      _removedItems.Clear(); 
      _modifiedItems.Clear(); 
      foreach (var item in this) 
      { 
       item.AcceptChanges(); 
      } 

      _originalCollection = this.ToList(); 
      OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsChanged))); 
     } 

     public void RejectChanges() 
     { 
      foreach (var removedItem in _removedItems.ToList()) this.Add(removedItem); 
      foreach (var addedItem in _addedItems.ToList()) this.Remove(addedItem); 
      foreach (var modifiedItem in _modifiedItems.ToList()) modifiedItem.RejectChanges(); 

      OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsChanged))); 
     } 

     protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
     { 
      var added = this.Where(current => _originalCollection.All(orig => orig != current)); 
      var removed = _originalCollection.Where(orig => this.All(current => current != orig)); 

      var modified = this.Except(added).Except(removed).Where(item => item.IsChanged).ToList(); 

      AttachItemPropertyChangedHandler(added); 
      DetachItemPropertyChangedHandler(removed); 

      UpdateObservableCollection(_addedItems, added); 
      UpdateObservableCollection(_removedItems, removed); 
      UpdateObservableCollection(_modifiedItems, modified); 

      base.OnCollectionChanged(e); 
      OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsChanged))); 
      OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsValid))); 
     } 

     private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      if (e.PropertyName == nameof(IsValid)) 
      { 
       OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsValid))); 
      } 
      else 
      { 
       var item = (T)sender; 
       if (_addedItems.Contains(item)) 
       { 
        return; 
       } 

       if (item.IsChanged) 
       { 
        if (!_modifiedItems.Contains(item)) 
        { 
         _modifiedItems.Add(item); 
        } 
       } 
       else 
       { 
        if (_modifiedItems.Contains(item)) 
        { 
         _modifiedItems.Remove(item); 
        } 
       } 

       OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsChanged))); 
      } 
     } 

     private void AttachItemPropertyChangedHandler(IEnumerable<T> items) 
     { 
      foreach (var item in items) 
      { 
       item.PropertyChanged += ItemPropertyChanged; 
      } 
     } 

     private void DetachItemPropertyChangedHandler(IEnumerable<T> items) 
     { 
      foreach (var item in items) 
      { 
       item.PropertyChanged -= ItemPropertyChanged; 
      } 
     } 

     private void UpdateObservableCollection(ObservableCollection<T> collection, IEnumerable<T> items) 
     { 
      collection.Clear(); 
      foreach (var item in items) 
      { 
       collection.Add(item); 
      } 
     } 
    } 
} 

https://github.com/PFC-acl-amg/GamaPFC/blob/master/GamaPFC/Core/ChangeTrackingCollection.cs

+1

Merci, mais j'ai ajouté quelques détails supplémentaires à ma question. –

+0

le lien fourni ne fonctionne pas, après la recherche utilisez ceci à la place: https://github.com/PFC-acl-amg/GamaPFC/blob/master/GamaPFC/Core/MVVM/ChangeTrackingCollection.cs – toumir