2012-03-16 12 views
5

Quando si crea un modello di vista in MVC3 di asp.net, dove dovrebbe andare il codice per creare un'istanza degli oggetti di quel modello di vista? Lo sto facendo principalmente nel controller in questo momento, a parte il codice per interrogare il database. Ecco un esempio in codice:Dove dovrebbe andare il codice per costruire i modelli di visualizzazione?

Vista Modello:

public class WorkListVM 
{ 
    //list for employees 
    [Display(Name = "Select A Employee")] 
    [Required] 
    public int? EmployeeId { get; set; } 
    public GenericSelectList EmployeeList { get; set; } 
} 

Codice Controller:

 //build view model 
     var vm = new WorkListVM(); 

     //build employee list 
     vm.EmployeeList = new GenericSelectList(0,"-- Select Employee --"); 
     var employees = new List<Employee>(); 
     using (var gr = new GenericRepo<Employee>()) 
     { 
      employees = gr.Get().ToList(); 
     } 
     foreach(var employee in employees) 
     { 
      var gl = new GenericListItem(); 
      gl.Id = employee.EmployeeId; 
      gl.DisplayFields = employee.FirstName + " " + employee.LastName; 
      vm.EmployeeList.Values.Add(gl); 
     } 

Generico Select List è una semplice classe per contenere i dati che va nella aiutante @html.dropdownfor 's SelectList. Costruisco queste liste di selezione e creo anche configurazioni di dati simili per i modelli di visualizzazione all'interno del codice del controller. Il controller che ospita questo codice ha un totale di 109 linee di codice, quindi non è enorme o fuori controllo. Tuttavia, cerco sempre di ridurre la ridondanza e talvolta il codice in //build employee list finisce per essere incollato sulla copia (ugh, odio il copia incolla) in altri controller.

C'è un posto migliore per avere questo codice? Dovrei forse usare lo schema di fabbrica per costruire i dati per queste liste di selezione/altri oggetti di dati di visualizzazione?

EDIT

Grazie per tutto il vostro aiuto. Ecco cosa ho finito per fare. Ho finito per fare un metodo all'interno della classe lista di selezione generica molto simile al .ToSelectList (...) suggerito da Richard e Jesse:

public class GenericSelectList 
{ 
    public List<GenericListItem> Values { get; set; } 
    public int StartValue { get; set; } 
    public string Message { get; set; } 

    public GenericSelectList(int StartValue = 0, string Message = "select") 
    { 
     Values = new List<GenericListItem>(); 
     this.StartValue = StartValue; 
     this.Message = Message; 
    } 

    public void BuildValues<T>(List<T> items, Func<T, int> value, Func<T, string> text) where T : class 
    { 
     this.Values = items.Select(f => new GenericListItem() 
     { 
      Id = value(f), 
      DisplayFields = text(f) 
     }).ToList(); 
    } 
} 
+0

Se si restituisce il tuo viewmodel a una vista, quindi il codice va nel controller. –

+0

@ 白 ジ ェ ー ム ス - Sì, alla fine di questo metodo actionresult è 'return View (vm);'. La vista è fortemente scritta '@model WorkListVM'. Alcuni di questo codice sono usati più volte (in diversi controller). Non avrebbe senso avere una fabbrica che avvolge la creazione di questi oggetti viewmodel al fine di ridurre la ridondanza? –

risposta

2

Se la logica di business che va a creare un modello di vista è piuttosto complessa io in genere estrarre in un metodo di supporto che posso testare indipendentemente dalla regolazione.

Tuttavia, a parte questo, la creazione del modello di visualizzazione è perfettamente soddisfacente all'interno del controller così com'è. Come già notato, come è possibile semplificare la generazione dell'elenco di selezione (per non parlare della possibilità di riutilizzare).

Ecco un'estensione ToSelectList di IEnumerable insieme ad un esempio di utilizzo:

public static List<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, string> value, Func<T, string> text, string defaultOption) 
{ 
    var items = enumerable.Select(f => new SelectListItem() 
              { 
               Text = text(f) , 
               Value = value(f) 
              }).ToList(); 

    if (!string.IsNullOrEmpty(defaultOption)) 
    { 
        items.Insert(0, new SelectListItem() 
         { 
          Text = defaultOption, 
          Value = string.Empty 
         }); 
    } 

    return items; 
} 

All'interno del vostro modello di vista si potrebbe aggiungere una proprietà in questo modo:

IEnumerable<SelectListItem> Employees { get; set; } 

Entro il controller (i' m supponendo che il repo sta tornando IEnumberable):

var employees = new IEnumerable<Employee>(); 
using (var gr = new GenericRepo<Employee>()) 
{ 
    employees = gr.Get(); 
} 

vm.Employees = employees.ToSelectList(x=>x.FirstName + " " + x.LastName, x=>x.Id, "-- Select Employee --") 

E poi per l'installazione il tuo elenco a discesa nella vista sarebbe qualcosa di simile:

@Html.DropDownListFor(model => model.EmployeeId, Model.employees) 
+0

+1 - Le grandi menti pensano allo stesso modo;) E mi hai salvato una traduzione! – RichardW1001

+0

Estensione molto ben scritta, grazie per l'input su questo argomento :) –

+0

@Jesse - La tua idea per un'estensione era buona, ma ho deciso di farne un metodo all'interno della classe che utilizzo per elenchi selezionati che ha funzionato alla grande! Vedi la mia modifica per l'implementazione. Grazie ancora! –

2

Se il vostro obiettivo è quello di evitare il codice ridondante, è necessario estrarre il comune parti in metodi di supporto. Nella forma più semplice, puoi usare metodi statici per questo.

I modelli di vista edificio nel controller sono generalmente l'approccio corretto, tranne se ci sono ragioni particolari per impedirlo.

È possibile strutturare il codice come preferisci. Utilizzare tecniche standard per far fronte a complessità quali metodi di supporto e astrazioni (con parsimonia). Non c'è bisogno di complicarsi quando è sufficiente la semplicità.

2

1 - È possibile aggiungere un metodo di estensione a IEnumerable<Employee> o IQueryable<Employee> che restituisce lo GenericSelectList. Vantaggi: è possibile riutilizzare qualsiasi raccolta di dipendenti, ad esempio un elenco filtrato e una sintassi di chiamata piuttosto piacevole; è possibile personalizzare il modo in cui l'elenco di selezione viene formato caso per caso. Svantaggi: dovrai scrivere uno di questi metodi per ogni tipo.

2 - È possibile modificare tale metodo di estensione per lavorare con un generico, che funziona contro IEnumerable<T> e produce uno GenericSetList basato su input Expression. Vantaggi - Il tuo metodo ora è veramente generico, cioè scrivi una volta e riutilizzalo. È possibile farlo in modo che combini con 1 in modo che la funzione 1 per classe utilizzi il generico per salvare la duplicazione. Svantaggi - assume conforto nell'uso di espressioni e simili.

3 - È possibile avere metodi di fabbrica che restituiscono ViewModels.

Queste cose possono anche funzionare in combinazione, e dovrebbero aiutare a rimuovere il codice copia/incolla e promuovere il riutilizzo e testabilità.

Modifica - ecco un modo che no. 2 potrebbe essere implementato (in VB ma la traduzione in C# è banale).Vorrei quindi aggiungere sovraccarichi per le utili permutazioni degli input.

Imports System.Runtime.CompilerServices 
Imports System.Linq.Expressions 

Module IQueryableExtensions 

    <Extension()> 
    Public Function ToSelectList(Of T)(source As IEnumerable(Of T), nameExpression As Expression(Of Func(Of T, String)), valueExpression As Expression(Of Func(Of T, String)), selectedExpression As Expression(Of Func(Of T, Boolean)), additionalItems As IEnumerable(Of SelectListItem)) As IEnumerable(Of SelectListItem) 
     Return additionalItems.Union(
      source.Select(Function(x) New SelectListItem With { 
           .Text = nameExpression.Compile.Invoke(x), 
           .Value = valueExpression.Compile.Invoke(x), 
           .Selected = selectedExpression.Compile.Invoke(x) 
          } 
        ) 
       ) 
    End Function 

End Module 

utilizzo Esempio:

Dim People As New List(Of Person) From {New Person With {.Name = "Richard", .ID = 1}} 

    Dim sl = People.ToSelectList(Function(p) p.Name, 
           Function(p) p.ID, 
           Function(p) p.ID = 1, 
           {New SelectListItem With {.Value = 0, 
                  .Text = "Please Select A Person"}}) 
+0

Potrei fare il n. 2, nel senso che aggiungerei una risposta generica per la generazione di elenchi selezionati. Tuttavia, richiederebbe una riflessione. Non sarebbe saggio usare il reflection nel repository? –

+0

Stai dicendo reflection perché vuoi includere il nome della classe? Ci sono altri modi per farlo, datemi un secondo e posterò un campione. – RichardW1001

+0

@TravisJ - ecco qua - una possibile implementazione senza riflessione. – RichardW1001

1

Non so se questo è il modo giusto, ma lo uso e mi aiuta a mantenere il controllo di tutto.

un controller nei miei occhi fa cose come sessioni di aggiornamento, cookie e quindi restituisce una vista. Non lo uso mai per ordinare dati o creare oggetti da inviare alla vista.

(potrei imbrogliare se un'una tantum uno di linea: p)

tutto quel genere di cose aggiungo ad una classe di supporto. Ogni volta che sento un copia e incolla, accendo, creo un nuovo metodo nel mio aiutante e lo chiamo invece.

in più, si ottiene un buon senso di benessere quando 3 giorni dopo è necessario utilizzare il metodo e il suo tutto lì in attesa.

Martyn

+1

uomo .. Io digito troppo lento, 2 risposte mentre sto toccando via! – SmithMart

+0

Quindi forse dovrei semplicemente creare un metodo di supporto generico usando la riflessione per gestire la creazione di questi elenchi di selezione ... –

Problemi correlati