web-dev-qa-db-fra.com

renderpartial avec le modèle null se voit passé le mauvais type

J'ai une page:

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

Et là-dessus, les éléments suivants:

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

Voici l'objet DTO:

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

et voici le partiel:

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

Lorsque Model.Tasks n'est pas null, tout fonctionne correctement. Cependant quand c'est nul je reçois:

L'élément de modèle transmis dans le dictionnaire est de type 'DTOSearchResults', mais ce dictionnaire nécessite un élément de modèle de type 'System.Collections.Generic.IEnumerable`1 [Task]'.

J'ai pensé qu'il ne fallait pas savoir quelle surcharge utiliser, alors j'ai fait ceci (voir ci-dessous) pour être explicite, mais j'ai toujours le même problème!

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

Je sais que je peux contourner ce problème en vérifiant la valeur null ou en ne transmettant même pas la valeur null, mais ce n'est pas le problème. Pourquoi cela arrive-t-il?

192
Andrew Bullock

Andrew Je pense que le problème que vous obtenez est le résultat de la méthode RenderPartial qui utilise le modèle de l'appelant (view) pour la vue partielle lorsque le modèle que vous transmettez est nul. Vous pouvez contourner ce problème étrange en procédant comme suit:

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

Est ce que ça aide?

340
meandmycode

La réponse de @ myandmycode est bonne, mais une réponse légèrement plus courte serait

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

Cela fonctionne parce que ViewDataDictionary est la chose qui contient le modèle et qu'il peut accepter un modèle en tant que paramètre constructeur. Cela passe essentiellement un dictionnaire de données de vues "entier", qui ne contient bien sûr que le modèle éventuellement nul.

45
configurator

Il semble que lorsque la propriété du modèle que vous transmettez est null, MVC revient intentionnellement au modèle "parent". Apparemment, le moteur MVC interprète une valeur de modèle nulle comme une intention d'utiliser la précédente.

Un peu plus de détails ici: ASP.NET MVC, vues fortement typées, paramètres de vue partielle glitch

24
Zack

Si vous ne voulez pas perdre votre ViewData précédent dans la vue partielle, vous pouvez essayer:

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

Une solution serait de créer un HtmlHelper comme ceci:

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);
}

La Partial<T>(...) correspondait avant la Partial(...) si pratique et aucune erreur d'ambiguïté lors de la compilation.

Personnellement, j'ai du mal à comprendre le comportement - semble difficile de l'imaginer en tant que choix de conception?

11
Colin Breame

Bien que cette question ait été résolue, je me suis heurté à cela et j'ai décidé de résoudre ce problème pour mon projet au lieu de le contourner avec new ViewDataDictionary().

J'ai créé un ensemble de méthodes d'extension: https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
J'ai également ajouté des méthodes qui n'appellent pas le partiel si le modèle est null, cela économisera beaucoup d'instructions if.

Je les ai créées pour Razor, mais deux d'entre elles devraient également fonctionner avec des vues de style aspx (celles qui utilisent HelperResult ne sont probablement pas compatibles).

Les méthodes d'extension ressemblent à ceci:

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

Il y a aussi des méthodes pour IEnumerable<object> Les modèles et les modèles rejetés peuvent également être appelés avec un Razor lambda qui vous permet d’envelopper le résultat partiel avec du HTML.

N'hésitez pas à les utiliser si vous le souhaitez.

11
Jaap

Ma solution consiste à:


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