2013-03-26 8 views
10

The Big Picture:Passo sottoclasse di modello generico di vista rasoio

ho trovato quello che sembra una limitazione del Razor e sto avendo problemi a venire con un buon modo intorno ad esso.

I Giocatori:

Diciamo che ho un modello come questo:

public abstract class BaseFooModel<T> 
    where T : BaseBarType 
{ 
    public abstract string Title { get; } // ACCESSED BY VIEW 
    public abstract Table<T> BuildTable(); 

    protected Table<T> _Table; 
    public Table<T> Table // ACCESSED BY VIEW 
    { 
     get 
     { 
      if (_Table == null) 
      { 
       _Table = BuildTable(); 
      } 
      return _Table; 
     } 
    } 
} 

E una sottoclasse come questo:

public class MyFooModel : BaseFooModel<MyBarType> 
{ 
    // ... 
} 

public class MyBarType : BaseBarType 
{ 
    // ... 
} 

voglio essere in grado di passare MyFooModel in una vista del rasoio che è definita in questo modo:

// FooView.cshtml 
@model BaseFooModel<BaseBarType> 

Ma, questo non funziona. Ottengo un errore in fase di esecuzione dicendo che FooView si aspetta BaseFooModel<BaseBarType> ma ottiene MyFooModel. Ricordiamo che MyFooModel in herits da BaseFooModel<MyBarType> e MyBarType eredita da BaseBarType.

quello che ho provato:

ho provato questo in terreni non rasoio per vedere se lo stesso è vero, che è. Ho dovuto usare un parametro template nella vista per farlo funzionare. Ecco questo punto di vista non rasoio:

public class FooView<T> 
    where T : BaseBarType 
{ 
    BaseFooModel<T> Model; 
    public FooView(BaseFooModel<T> model) 
    { 
     Model = model; 
    } 
} 

Con tale struttura, il seguente funziona:

new FooView<MyBarType>(new MyFooModel()); 

La mia domanda:

Come posso farlo con Razor ? Come posso passare un tipo come sto facendo con FooView?
Non riesco a, ma c'è un modo per aggirare questo? Posso ottenere la stessa architettura in qualche modo?

Fammi sapere se posso fornire ulteriori informazioni. Sto utilizzando .NET 4 e MVC 3.


EDIT:
Per ora, sto solo aggiungendo una vista rasoio per ogni sottoclasse di BaseFooModel<BaseBarType>. Non ne sono entusiasta perché non voglio creare una nuova vista ogni volta che aggiungo un nuovo modello.

L'altra opzione è sfruttare il fatto che I am è in grado di farlo funzionare in normali classi C# senza rasoio. Potrei avere il mio rasoio vista @inherits la vista C# e quindi chiamare qualche metodo di rendering. Non mi piace questa opzione perché non mi piace avere due modi di renderizzare html.

Altre idee?So che è difficile capire il contesto del problema quando sto dando nomi di classe con Foo e Bar, ma non posso fornire troppe informazioni poiché è un po 'sensibile. Le mie scuse su questo.


Quello che ho finora, con la risposta di Benjamin:

public interface IFooModel<out T> 
    where T : BaseBarModel 
{ 
    string Title { get; } 
    Table<T> Table { get; } // this causes an error: 
          // Invalid variance: The type parameter 'T' must be 
          // invariantly valid on IFooModel<T>.Table. 
          // 'T' is covariant. 
} 

public abstract class BaseFooModel<T> : IFooModel<T> 
    where T : BaseBarModel 
{ 
    // ... 
} 

Che ha finito per lavoro:

public interface IFooModel<out T> 
    where T : BaseBarModel 
{ 
    string Title { get; } 
    BaseModule Table { get; } // Table<T> inherits from BaseModule 
           // And I only need methods from BaseModule 
           // in my view. 
} 

public abstract class BaseFooModel<T> : IFooModel<T> 
    where T : BaseBarModel 
{ 
    // ... 
} 
+0

molto spesso trovo una risposta per il momento ho digitato la questione, o almeno poco dopo. non questa volta ... – lbstr

+1

qual è il tuo BaseFooModel - e cosa fa sulla pagina (inserimenti, solo visualizzazione, è cambiato, ecc.)? questo è rilevante (qualche codice di esempio) – NSGaga

+0

Questo è un buon punto, ma direi che dato che questa informazione viene usata in una vista, dovrebbe solo essere letta. Qualsiasi aggiornamento dovrebbe accadere in un metodo di azione del controllore. –

risposta

17

È necessario introdurre uno un'interfaccia con un parametro di tipo generico covariant nella vostra gerarchia di classe:

public interface IFooModel<out T> where T : BaseBarType 
{ 
} 

e derivano tuoi BaseFooModel dalla interfaccia di cui sopra.

public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType 
{ 
} 

nel controller:

[HttpGet] 
public ActionResult Index() 
{ 
    return View(new MyFooModel()); 
} 

Infine, aggiornare parametro del modello di immagine per essere:

@model IFooModel<BaseBarType> 
+0

Grazie Ben! Questo sta funzionando molto bene finora. Aspetterò prima di accettare finché non sono sicuro che funzionerà nella mia situazione. +1 – lbstr

+0

Ottimo per sentirlo! Ho avuto un problema simile nel mio codice quindi sono contento di essere stato in grado di usarlo per aiutare qualcun altro. Se incontri qualche problema, ti preghiamo di inviare ulteriori informazioni e cercherò di aiutarti se posso. –

+0

+1 Non stavo cercando di minare te - non sono così affamato - come ad es. Jon è :) – NSGaga

0

Il problema si verifica non è un errore di Razor , ma un errore C#. Prova a farlo con le classi e otterrai lo stesso errore. Questo perché il modello non è BaseFooModel<BaseBarType>, ma BaseFooModel<MyFooModel> e una conversione implicita non può avvenire tra i due. Normalmente, in un programma dovresti fare una conversione per farlo.

Tuttavia, con .NET 4, introdotto era contravariance and covariance, che suona come l'abilità di ciò che stai cercando. Questa è solo una funzionalità di .NET 4. e onestamente non so se Razor in .NET 4 ne faccia uso o meno.

+0

Grazie per il feedback Brian. Hai ragione; Ho visto la stessa cosa con le classi, che è quello che ho cercato di spiegare sopra con il bit 'FooView '. Aggiungere un template param era l'unico modo per farlo funzionare, il che ovviamente non si traduce in Razor. Puoi spiegare cosa intendevi quando hai detto che la lista non è "IEnumerable ", ma "IEnumerable ". Quale lista? – lbstr

+0

cosa 'IEnumerable'? Il modello dovrebbe essere un singolo 'MyFooModel', non una collezione. – lbstr

+0

Err, sono davvero fuori posto, intendevo BaseFooModel, quindi ho aggiornato per posta sopra. –

1

Utilizzando modelli di interfacce basate su stato volutamente cambiato tra ASP.NET MVC 2 e MVC 3.

You can see here

MVC Team:

Avere modelli basati su interfaccia non è qualcosa che incoraggiamo (né, date le limitazioni imposte dalla correzione di bug, può realisticamente supportare). Passare a classi base astratte risolverebbe il problema.

"Scott Hanselman"

Problemi correlati