2017-09-17 4 views
1

J'ai porté mon application pour utiliser la base de données Realm au lieu de SQLite. Cependant, je rencontre un problème avec le IRealmCollection en combinaison avec MVVM.Quelle est la meilleure façon d'utiliser ViewModel avec Realm?

Imaginons le modèle suivant:

public class Item : RealmObject 
{ 
    public string Id { get; } 
} 

L'approche actuelle sur mon MainViewModel est celui-ci:

public class MainViewModel 
{ 
    public IRealmCollection<Item> { get; private set; } 
} 

Le problème que je suis maintenant face est que le Item a des commandes spécifiques, situées dans un ItemViewModel. Je veux utiliser ceux-ci tout en gardant les caractéristiques du IRealmCollection.

Le ItemViewModel:

public class ItemViewModel 
{ 
    public Item Model { get; private set; } 
    public ICommand MyCommand { get; private set; } 
} 

Il existe plusieurs méthodes que j'ai pensé à, mais je me suis demandé s'il y a une solution plus simple à cette:

  • Créer une coutume ObservableCollection que s'abonne au domaine
  • Utilisez l'extension LINQ .Select() pour créer un ViewModel en dehors de la liste
  • Sa ve le ItemViewModel dans le royaume et ajoutez l'attribut [Ignored] à chaque propriété supplémentaire ou commande
  • Déplacer la logique de la ItemViewModel à une classe distincte (comme un ItemHelper) et appeler de la MainViewModel

Quelle est la meilleur et probablement le plus MVVM-façon de résoudre ce problème?

Répondre

1

Si vos commandes et propriétés supplémentaires sont simples et peuvent être incorporées dans le Item persistant, j'irais avec cette solution. Cela peut ne pas toujours fonctionner, par exemple, car les objets RealmObjects sont créés dynamiquement lors de l'accès à l'index dans la collection. .: par exemple

var collection = realm.All<Item>(); 
var a = collection.ElementAt(0); 
var b = collection.ElementAt(0); 

vous obtiendrez différentes instances pour a et b. Cela signifie que vous ne pouvez pas compter sur l'état non-persistant dans vos héritiers RealmObject. Si cela est un dealbreaker, alors je suggère un ObservableCollection personnalisé qui enveloppe IRealmCollection et traite des projections à votre ViewModel qui peuvent utiliser ensuite la composition pour exposer la RealmObject:

public interface IViewModel<T> where T : RealmObject 
{ 
    T Item { get; set; } 
} 

public class MyObservableCollection<TViewModel, TRealmObject> : IReadOnlyList<TViewModel>, INotifyCollectionChanged 
    where TRealmObject : RealmObject 
    where TViewModel : IViewModel<TRealmObject>, new() 
{ 
    private readonly IRealmCollection<TRealmObject> _collection; 

    public TViewModel this[int index] => Project(_collection[index]); 

    private event PropertyChangedEventHandler _propertyChanged; 
    public event NotifyCollectionChangedEventHandler CollectionChanged 
    { 
     add 
     { 
      UpdateCollectionChangedSubscriptionIfNecessary(isSubscribed: true); 
      _collectionChanged += value; 
     } 
     remove 
     { 
      _collectionChanged -= value; 

      UpdateCollectionChangedSubscriptionIfNecessary(isSubscribed: false); 
     } 
    } 

    public MyObservableCollection(IRealmCollection<TRealmObject> collection) 
    { 
     _collection = collection; 
    } 

    private TViewModel Project(TRealmObject obj) 
    { 
     return new TViewModel 
     { 
      Item = obj 
     }; 
    } 

    private void UpdateCollectionChangedSubscriptionIfNecessary(bool isSubscribed) 
    { 
     if (_collectionChanged == null) 
     { 
      if (isSubscribed) 
      { 
       // Subscribe via _collection 
      } 
      else 
      { 
       // Unsubscribe via _collection 
      } 
     } 
    } 
}