renderpartial con modello null ottiene il tipo sbagliato


198

Ho una pagina:

<%@ Page Inherits="System.Web.Mvc.View<DTOSearchResults>" %>

E su di esso, il seguente:

<% Html.RenderPartial("TaskList", Model.Tasks); %>

Ecco l'oggetto DTO:

public class DTOSearchResults
{
    public string SearchTerm { get; set; }
    public IEnumerable<Task> Tasks { get; set; }

ed ecco il parziale:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Task>>" %>

Quando Model.Tasks non è null, tutto funziona correttamente. Tuttavia quando è nullo ottengo:

L'elemento del modello passato nel dizionario è di tipo 'DTOSearchResults' ma questo dizionario richiede un elemento del modello di tipo 'System.Collections.Generic.IEnumerable`1 [Task]'.

Ho pensato che non dovesse sapere quale sovraccarico usare, quindi l'ho fatto (vedi sotto) per essere esplicito, ma ho ancora lo stesso problema!

<% Html.RenderPartial("TaskList", (object)Model.Tasks, null); %>

So che posso aggirare il problema controllando null, o nemmeno passando null, ma non è questo il punto. Perché sta succedendo?

Risposte:


349

Andrew Penso che il problema che stai riscontrando sia il risultato del metodo RenderPartial che utilizza il modello di chiamata (vista) per la vista parziale quando il modello che passi è nullo .. Puoi aggirare questo strano comportamento facendo:

<% Html.RenderPartial("TaskList", Model.Tasks, new ViewDataDictionary()); %>

Questo aiuta?


16
Risparmio ancora tempo alle persone. Mi stavo strappando i capelli.
James Gregory,

3
Capisco perché supportano il modello null e il passaggio delle pagine Model ma non sono riusciti a gestirlo sovraccaricandolo. @ Html.Render ("asini") è diverso da @ Html.Render ("asini", couldbenull)
Phil Strong

19
Lo trovo molto controintuitivo, quindi ho aggiunto un "problema", votalo
pbz,

3
Ho scoperto che con questa soluzione il mio ValidationSummary nella mia vista parziale non funzionava perché i ViewData del modello primario andavano persi nella vista parziale. Ho usato la risposta fornita qui stackoverflow.com/a/12037580/649497 per risolvere questo problema.
BruceHill,

5
Dovresti passare il ViewData esistente: nuovo ViewDataDictionary (ViewData)
ScottE

48

La risposta di myandmycode è buona, ma sarebbe leggermente più breve

<% Html.RenderPartial("TaskList", new ViewDataDictionary(Model.Tasks)); %>

Questo funziona perché ViewDataDictionaryè l'elemento che contiene il modello e può accettare un modello come parametro del costruttore. Questo in sostanza passa un "intero" dizionario dei dati di vista, che ovviamente contiene solo il modello possibilmente nullo.


2
@jcmcbeth: Ehm, no, non ... Ho usato questo codice esatto con valori null con successo.
configuratore

1
@jcmcbeth: stai usando new ViewDataDictionary(null)? Perché ciò sceglierebbe un sovraccarico diverso, uno con un ViewDataDictionaryparametro, che probabilmente non accetterebbe null.
configuratore

1
Sembrerebbe che l'utilizzo di una proprietà ViewBag causi la chiamata del costruttore errato. Il modo in cui prende un tipo dinamico e assume che sia ViewDataDictionary su un oggetto non ha senso per me, ma sembra essere quello che sta facendo. Dovrai lanciarlo su un oggetto affinché selezioni il costruttore corretto.
Joel McBeth,

1
@jcmcbeth: chiamarlo su un tipo dinamico usa lo stesso di se hai dato il valore effettivo; se il valore è null, che è la stessa vocazione new ViewDataDictionary(null)che provoca il sovraccarico di specifica più di essere chiamato.
configuratore

1
se lo usi in questo modo, l'errore di dizione scompare .. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks))Stai usando il costruttore sbagliato se è nullo.
Filip Cornelissen,

26

Sembra che quando la proprietà del modello che si sta passando è null MVC ritorna intenzionalmente al modello "genitore". Apparentemente il motore MVC interpreta un valore di modello nullo come intenzione di utilizzare quello precedente.

Leggermente maggiori dettagli qui: ASP.NET MVC, viste fortemente tipizzate, glitch dei parametri della vista parziale


1
+1 per aver effettivamente cercato di spiegare il problema e non solo per trattarlo come un comportamento strano
YavgenyP

Sì, questo mi stava succedendo e quanto sopra non lo ha risolto, mi ha solo dato un po 'più informazioni sul mio errore reale.
Tela

20

Se non vuoi perdere i ViewData precedenti nella vista parziale, puoi provare:

<% Html.RenderPartial("TaskList", Model.Tasks, new ViewDataDictionary(ViewData){Model = null});%>

1
Questo non sembra rispondere alla domanda.
John Saunders,

6
+1 In realtà funziona. È sostanzialmente la stessa idea presentata qui stackoverflow.com/a/713921/649497 ma risolve un problema con quella risposta e cioè che ViewData scomparirà se si crea un'istanza di ViewDataDictionary con un costruttore vuoto. Ho prima risolto questo problema con la soluzione accettata e poi ho scoperto che il mio ValidationSummary non funzionava nella vista parziale. Questa soluzione ha risolto questo per me. Questa risposta richiede un maggiore riconoscimento per risolvere il problema e preservare ViewData nella vista parziale.
BruceHill,

1
@Franc P in realtà ha funzionato senza perdere i valori ViewBag e quindi ha passato un modello nullo. Grazie.
Zaker,

Questa è la risposta giusta se hai bisogno dell'accesso ViewBag nei tuoi Partial!
Daniel Lorenz,

12

Una soluzione sarebbe quella di creare un HtmlHelper in questo modo:

public static MvcHtmlString Partial<T>(this HtmlHelper htmlHelper, string partialViewName, T model)
{
    ViewDataDictionary viewData = new ViewDataDictionary(htmlHelper.ViewData)
    {
        Model = model
    };
    return PartialExtensions.Partial(htmlHelper, partialViewName, model, viewData);
}

Il Partial<T>(...) abbinato prima del Partial(...)così conveniente e nessun errore durante la compilazione di ambiguità.

Personalmente trovo difficile capire il comportamento - sembra difficile immaginarlo come una scelta progettuale?


1
questo è quello che ho fatto alla fine. in asp.net mvc non ci sono molte scelte / comportamenti di design che abbiano un senso. da quando l'ha abbandonato. utile ad altri, quindi prendi un +1
Andrew Bullock,

Buono, comunque poco chiaro per l'utente. Diciamo che sono abituato a ciò che il mio colleage usa nel suo progetto, ne inizio uno nuovo. Quindi dimentica totalmente di aggiungere questo sovraccarico e voilà, le eccezioni iniziano a verificarsi in produzione perché non l'abbiamo testato abbastanza bene. Un altro nome è beter imho.
Jaap

11

Anche se è stata data una risposta, mi sono imbattuto in questo e ho deciso di voler risolvere questo problema per il mio progetto invece di aggirarlo new ViewDataDictionary().

Ho creato una serie di metodi di estensione: https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
Ho anche aggiunto alcuni metodi che non chiamano il parziale se il modello è null , questo salverà molte dichiarazioni if.

Li ho creati per Razor, ma un paio di essi dovrebbero funzionare anche con viste in stile aspx (quelli che usano HelperResult probabilmente non sono compatibili).

I metodi di estensione sono simili al seguente:

@* calls the partial with Model = null *@
@Html.PartialOrNull("PartialName", null)
@* does not call the partial if the model is null *@
@Html.PartialOrDiscard("PartialName", null)

Esistono anche metodi per i IEnumerable<object>modelli e quelli di scarto possono anche essere chiamati con un lambda Razor che consente di racchiudere il risultato parziale con un po 'di html.

Sentiti libero di usarli se vuoi.


1
Ancora utile a partire da MVC5: 25/06/2014. Grazie.
Jason,

1

La mia soluzione a questo è:


<% Html.RenderPartial("TaskList", Model.Tasks ?? new List()); %>


Questa è una soluzione sporca. Dal tuo punto di vista parziale dovresti essere in grado di verificare il modello null, piuttosto che verificare se l'elenco ha valori e se è null.
madd
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.