2015-04-09 17 views
5

Sto riscontrando un problema con una raccolta polimorfica di ViewModels nella mia applicazione MVC. Ho ricevuto questo tramite una chiamata al servizio web e ho bisogno di scorrere attraverso di loro e dare loro la propria vista parziale, in base al tipo di oggetto.Polymorphic ViewModel raccolta e rendering in MVC Visualizzazioni parziali

public abstract class ProvinceViewModel 
{ 
    public string Code { get; set; } 
} 

public sealed class OntarioViewModel : ProvinceViewModel { } 

public sealed class QuebecViewModel : ProvinceViewModel {} 

Dal mio punto di vista sto provando a scorrere tra loro e assegnare una vista parziale. Devo fare un sacco di casting qui per farlo funzionare. Se provo a spostarlo su un'azione del controller e a passare al tipo astratto, otterrò un errore che non possiamo creare un'istanza di classe astratta.

ICollection<ProvinceViewModel> ProvinceList; // collection receive via service 

@for (int i = 0, c = ProvinceList.Count; i < c; i++) 
{ 
    var currentProvince = this.Model.ElementAt(i); 
    @switch (additionalRegistry.Code) 
    { 
     case "QC": 
      @Html.Partial("AlbertaDetail", (QuebecViewModel)currentProvince) 
      break;        
     case "ON": 
      @Html.Partial("OntarioDetail", (OntarioViewModel)currentProvince) 
      break; 
     default: 
      @Html.Partial("ProvinceDetail", ProvinceViewModel) 
      break; 
    } 
} 

Ho fortemente visualizzato tipo, in modo da poter accedere alle diverse proprietà.

Come potrei risolvere il problema in modo più elegante? Dovrei creare una nuova classe di base surrogata per la classe astratta per creare un'istanza di essa più semplice?

+0

si potrebbe avere nella pagina 'ProvinceDetail' fare la commutazione invece per trattare le diverse province in modo appropriato invece?Questo ti permetterebbe di mantenere la logica su quella pagina invece di doverlo ripetere se li usi altrove – DLeh

+1

Questo non è chiaro: devi istanziare una vista parziale per ogni * tipo concreto * di ProvinceDetail, o ogni * istanziazione * ? Sembra strano che tu abbia più viste parziali QuebecDetail, per esempio. – Kjata30

+0

Scusa, ho provato a semplificare l'esempio. Ho bisogno di istanziare una vista parziale per ogni provincia che ha alcune proprietà differenti. per esempio Ontario, Quebec, Manitoba, BC hanno tutti una vista parziale specifica e diversa, tutto il resto ottiene una vista generica predefinita standard, ad es. ProvinceDetail. Il risultato sarà una lista di Province e i loro dettagli nella pagina principale. – mflair2000

risposta

0

Avevo un requisito simile ed è così che sono riuscito a risolvere questo problema. My viewmodel (BusinessEventEmailViewModel) ha un elenco di interfacce (IBusinessEventEmail) risolto in runtime con unità. Un IBusinessEventEmail ha una proprietà EventCode.

public class BusinessEventEmailViewModel : MailHeaderViewModel 
{ 
    #region members 
    public List<IBusinessEventEmail> Events { get; set; } 

Ritengo renderò la vista parziale utilizzando una convenzione di denominazione:

Html.RenderPartial("~/Views/Shared/Email/_" + businessEvent.EventCode + ".cshtml", businessEvent); 

Poi, ho un XXXEventEmail attuazione IBusinessEventEmail con l'EventCode XXX e una vista parziale _XXX.cshtml

1

Dopo aver incontrato lo stesso problema in passato, ho creato la seguente soluzione:

Innanzitutto, decorare il modello di visualizzazione (concreto) conAttributoche indica il nome della vista da utilizzare. Per esempio:

[ExportMetadata("View", "Ontario")] 
public sealed class OntarioViewModel : ProvinceViewModel { } 

[ExportMetadata("View", "Quebec")] 
public sealed class QuebecViewModel : ProvinceViewModel {} 

quindi estendere la vostra HtmlHelper con il Partial seguente metodo:

public static MvcHtmlString Partial<T>(this HtmlHelper htmlHelper, T model, string prefix = null) 
{ 
    var modelType = typeof (T); 
    var partialAttr = modelType.GetCustomAttributes<ExportMetadataAttribute>().SingleOrDefault(x => x.Name == "View"); 

    if (partialAttr == null) 
     throw new Exception(modelType.Name + " doesn't define any view to be used"); 

    var partialName = (prefix ?? String.Empty) + partialAttr.Value; 

    return htmlHelper.Partial(partialName, model, htmlHelper.ViewData); 
} 

Poi usarlo:

@Html.Partial(currentProvince); 

E nel caso in cui i parziali risiedono in alcuni sub-directory:

@Html.Partial(currentProvince, "Partials/") 

(Se hai bisogno di aiuto per registrare l'helper HTML personalizzato vedi https://stackoverflow.com/a/5052790)

0

È possibile ottenere ciò con i modelli di visualizzazione. Creare un modello di visualizzazione per ogni tipo nella cartella DisplayTemplates all'interno Visualizzazioni directory del controller:

+-- Views 
    +-- Provinces 
     +-- DisplayTemplates 
      +-- OntarioViewModel.cshtml 
      +-- QuebecViewModel.cshtml 

display ogni modello utilizzando l'helper DisplayFor nella vista:

@model ICollection<ProvinceViewModel> 

@foreach (var province in Model) 
{ 
    @Html.DisplayFor(_ => province) 
}