ereditarietà viewmodel e riferimenti modello duplicati

La mia domanda è: come gestire una catena ereditaria di viewmodels?

La mia situazione:

Sto avendo un ViewModelBase standard che implementa solo l’interfaccia INotifyPropertyChanged.

Inoltre ho un BusinessObjectViewModel che ha un Guid, un PersonBaseViewModel che ha i dati principali delle persone, un CustomerViewModel con materiale relativo ai clienti e un EmployeeViewModel con materiale dipendente.

Tutti i viewmodels incapsulano certamente un modelobject (Customer, Employee, PersonBase).

  • BusinessObjectViewModel eredita da ViewModelBase
  • PersonBaseViewModel eredita da BusinessObjectViewModel
  • CustomerViewModel eredita da PersonBaseViewModel
  • EmployeeViewModel eredita da PersonBaseViewModel

Il modello arriva tramite costruttore nel viewmodel.

Se utilizzo una catena di costruzione (ogni viewmodel effettua una chiamata al costruttore base) ogni viewmodel ha il suo modello per restituire i valori incapsulati dal modello.

Ma devo avere una proprietà Model in ogni viewmodel. Nel caso di CustomerViewModel avrei un riferimento in CustomerViewModel , uno in PersonBaseViewModel e uno in BusinessObjectViewModel per uno e lo stesso object. Mi sembra stupido.

O devo trasmettere ogni accesso di proprietà nei modelli di visualizzazione superiore.

ps questo è solo un piccolo ritaglio della mia gerarchia di modelli.

Grazie in anticipo.

Se le classi BusinessObject e Person (e le loro controparti VM) sono astratte, è ansible accedere al modello corretto in questo modo:

public abstract class BusinessObjectViewModel : ViewModelBase { protected abstract BusinessObject BusinessObject { get; } protected BusinessObject Model { get { return this.BusinessObject; } } } public abstract class PersonViewModel : BusinessObjectViewModel { protected abstract Person Person { get; } protected new Person Model { get { return this.Person; } } protected override sealed BusinessObject BusinessObject { get { return this.Model; } } } public class CustomerViewModel : PersonViewModel { protected new Customer Model { get; set; } protected override sealed Person Person { get { return this.Model; } } } public class EmployeeViewModel : PersonViewModel { protected new Employee Model { get; set; } protected override sealed Person Person { get { return this.Model; } } } 

In questo modo, ogni class VM derivata fornisce un valore per la sua proprietà di base del modello VM implementando la proprietà astratta e nasconde una proprietà Model della class base, in modo che ogni VM lavori con la proprietà Model di un tipo appropriato (quindi non è richiesta alcuna trasmissione).

Questo approccio ha i suoi vantaggi e svantaggi:

Benefici:

  • Nessun casting coinvolto

svantaggi:

  • Funziona solo se le classi base (BusinessObjectViewModel e PersonViewModel) sono astratte perché deve esistere una proprietà astratta implementata dalla class derivata e fornisce un’istanza Model a queste classi base.
  • Non è ansible accedere alla proprietà del modello nei costruttori della class base, poiché il concatenamento del costruttore passa dalla class base alla class più derivata. Il costruttore della class più derivato imposterà Model, quindi i costruttori della class base sono chiamati a vederlo presto. Questo può essere evitato passando Model come parametro attraverso i costruttori.
  • Le proprietà BusinessObject e Person non sono necessarie viste dalle classi derivate. EditorBrowsableAttribute potrebbe essere d’aiuto qui per Intellisense, ma solo quando il codice viene utilizzato da un altro assembly in diverse soluzioni di Visual Studio (questo è il comportamento specifico di Visual Studio).
  • Prestazione. Quando le classi di base accedono a Model, il codice passerà attraverso una catena di proprietà virtuali. Ma da quando le proprietà astratte sono state implementate come contrassegnate come sigillate, la ricerca della tabella virtuale non dovrebbe essere così dannosa per le prestazioni.
  • Non si adatta bene. Per le gerarchie di classi profonde il codice conterrebbe molti membri non necessari.

Un altro approccio sarebbe:

 public class BusinessObjectViewModel : ViewModelBase { protected BusinessObject Model { get; private set; } public BusinessObjectViewModel(BusinessObject model) { this.Model = model; } } public class PersonViewModel : BusinessObjectViewModel { protected new Person Model { get { return (Person)base.Model; } } public PersonViewModel(Person model) : base(model) { } } public class CustomerViewModel : PersonViewModel { protected new Customer Model { get { return (Customer)base.Model; } } public CustomerViewModel(Customer model) : base(model) { } } public class EmployeeViewModel : PersonViewModel { protected new Employee Model { get { return (Employee)base.Model; } } public EmployeeViewModel(Employee model) : base(model) { } } 

Benefici:

  • Le classi base non devono essere astratte.
  • È ansible accedere al modello tramite costruttori di classi di base.
  • Nessuna proprietà aggiuntiva non necessaria.

svantaggi:

  • Casting.

Basandomi su questa analisi, sceglierei una seconda opzione perché il suo unico difetto, le prestazioni di casting, sarebbe una micro-ottimizzazione non necessaria che non sarebbe evidente nel contesto WPF.

La risposta più semplice IMO è usare Generics, che potrebbe essere semplice come

 public abstract class ViewModelBase TModel : class{ public TModel Model { get; protected set; } } 

Il sistema di scrittura .net saprà che il tuo TModel è una persona, un cliente o qualsiasi altra cosa senza il casting.

Fammi sapere se hai bisogno di più o se vuoi pubblicare del codice che ha bisogno di aiuto. E sì, può essere difficile ottenere i tuoi supereroi gerarchia in un primo momento.

HTH,
Berryl

Se si desidera solo esporre la propria proprietà Model in ViewModels, non è necessario dichiarare nuovamente le proprietà Model in ViewModel per esporle. Espongo normalmente l’object Model sottostante come una proprietà nel mio ViewModels. Nel tuo caso, ad esempio nel tuo EmployeeViewModel avresti un:

 private Employee _MyEmployee; public Employee MyEmployee { get { return _MyEmployee; } set { _MyEmployee = value; NotifyPropertyChanged(x=>x.MyEmployee); } 

Quindi la tua vista potrebbe legarsi alle proprietà dei tuoi dipendenti attraverso la proprietà MyEmployee esposta nel ViewModel. Per quanto ho capito, l’unico caso in cui si desidera ripetere nuovamente o avvolgere le proprietà del modello nella VM è quando è necessario eseguire alcune manipolazioni dei dati per essere presentate alla vista.