renderpartial con modelo nulo pasa el tipo incorrecto


198

Tengo una pagina:

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

Y sobre esto, lo siguiente:

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

Aquí está el objeto DTO:

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

y aquí está el parcial:

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

Cuando Model.Tasks no es nulo, todo funciona bien. Sin embargo, cuando es nulo me sale:

El elemento modelo que se pasa al diccionario es del tipo 'DTOSearchResults' pero este diccionario requiere un elemento modelo del tipo 'System.Collections.Generic.IEnumerable`1 [Tarea]'.

Pensé que no debía saber qué sobrecarga usar, así que hice esto (ver más abajo) para ser explícito, ¡pero sigo teniendo el mismo problema!

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

Sé que puedo evitar esto al verificar nulo, o incluso no pasar nulo, pero ese no es el punto. ¿Por qué está pasando esto?

Respuestas:


349

Andrew Creo que el problema que está obteniendo es el resultado del método RenderPartial que usa el modelo de la llamada (vista) a la vista parcial cuando el modelo que pasa es nulo ... puede evitar este comportamiento extraño haciendo:

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

¿Eso ayuda?


16
Aún ahorrando tiempo a la gente. Me estaba sacando el pelo por esto.
James Gregory el

3
Entiendo por qué admiten el modelo nulo y pasan las páginas Modelo, pero no podrían haberlo manejado sobrecargando. @ Html.Render ("burros") es diferente de @ Html.Render ("burros", couldbenull)
Phil Strong

19
Esto me parece muy intuitivo, así que agregué un "problema", vote si está de acuerdo: aspnet.codeplex.com/workitem/8872
pbz

3
Descubrí que con esta solución mi ValidationSummary en mi vista parcial no funcionaba porque los ViewData del modelo primario se perdían en la vista parcial. Utilicé la respuesta dada aquí stackoverflow.com/a/12037580/649497 para resolver esto.
BruceHill

55
Debe pasar los ViewData existentes: nuevo ViewDataDictionary (ViewData)
ScottE

48

La respuesta de @ myandmycode es buena, pero una respuesta un poco más corta sería

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

Esto funciona porque ViewDataDictionaryes lo que contiene el modelo, y puede aceptar un modelo como parámetro constructor. Básicamente, esto pasa un diccionario de datos de vista "completo", que por supuesto solo contiene el modelo posiblemente nulo.


2
@jcmcbeth: Erm, no, no lo hace ... He usado este código exacto con nulos con éxito.
configurador

1
@jcmcbeth: ¿Estás usando new ViewDataDictionary(null)? Porque eso elegiría una sobrecarga diferente, una con un ViewDataDictionaryparámetro, que probablemente no aceptaría valores nulos.
configurador

1
Parece que usar una propiedad ViewBag hace que se llame al constructor incorrecto. La forma en que toma un tipo dinámico y supone que es un ViewDataDictionary sobre un objeto no tiene sentido para mí, pero parece ser lo que está haciendo. Tendrá que convertirlo en un objeto para que seleccione el constructor correcto.
Joel McBeth

1
@jcmcbeth: al llamarlo a través de un tipo dinámico se usa lo mismo que si se hubiera dado el valor real; si el valor es null, es lo mismo que llamar, lo new ViewDataDictionary(null)que provoca que se invoque la sobrecarga más específica.
configurador

1
si lo usa así, el error de dicción desaparece. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks))Está usando el constructor incorrecto si es nulo.
Filip Cornelissen

26

Parece que cuando la propiedad del Modelo que está pasando es nula, MVC intencionalmente vuelve al Modelo "principal". Aparentemente, el motor MVC interpreta un valor de modelo nulo como una intención de usar el anterior.

Un poco más de detalles aquí: ASP.NET MVC, vistas fuertemente tipadas, falla parcial de los parámetros de vista


1
+1 por tratar de explicar el problema, y ​​no solo tratar esto como un comportamiento extraño
YavgenyP

Sí, esto me estaba sucediendo y lo anterior no lo solucionó, solo me dio un poco más de información sobre mi error real.
Canvas

20

Si no desea perder sus ViewData anteriores en la vista parcial, puede intentar:

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

1
Esto no parece responder a la pregunta.
John Saunders

66
+1 En realidad funciona. Básicamente es la misma idea presentada aquí stackoverflow.com/a/713921/649497, pero supera un problema con esa respuesta y es que ViewData se perderá si crea una instancia de ViewDataDictionary con un constructor vacío. Primero resolví este problema con la solución aceptada y luego descubrí que mi ValidationSummary no funcionaba en la vista parcial. Esta solución lo resolvió para mí. Esta respuesta necesita más reconocimiento para resolver el problema y preservar ViewData en su vista parcial.
BruceHill

1
@Franc P esto realmente funcionó sin perder los valores de ViewBag y, por lo tanto, pasó un modelo nulo. Gracias.
Zaker

¡Esta es la respuesta correcta si necesita acceso a ViewBag en sus Parciales!
Daniel Lorenz

12

Una solución sería crear un HtmlHelper como este:

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>(...)coincidencia antes del Partial(...)error tan conveniente y sin ambigüedad al compilar.

Personalmente, me resulta difícil entender el comportamiento. ¿Parece difícil imaginar esto como una opción de diseño?


1
Esto es lo que hice al final. No hay muchas opciones de diseño / comportamientos en asp.net mvc que tengan sentido. Desde que lo abandonó. útil para otros, así que tenga un +1
Andrew Bullock

Buena, aunque poco clara para el usuario. Digamos que estoy acostumbrado a lo que mi colega usa en su proyecto, comienzo uno nuevo. Entonces, olvide totalmente agregar esta sobrecarga y listo, las excepciones comienzan a suceder en la producción porque no lo probamos lo suficientemente bien. Un nombre diferente es beter imho.
Jaap

11

Aunque esto ha sido respondido, me encontré con esto y decidí que quería resolver este problema para mi proyecto en lugar de solucionarlo new ViewDataDictionary().

Creé un conjunto de métodos de extensión: https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
También agregué algunos métodos que no llaman al parcial si el modelo es nulo , esto ahorrará muchas declaraciones if.

Los creé para Razor, pero algunos de ellos también deberían funcionar con vistas de estilo aspx (las que usan HelperResult probablemente no sean compatibles).

Los métodos de extensión se ven así:

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

También hay métodos para IEnumerable<object>modelos y los de descarte también se pueden llamar con una Razor lambda que le permite envolver el resultado parcial con algo de html.

Siéntase libre de usarlos si lo desea.


1
Sigue siendo útil a partir de MVC5: 25/06/2014. Gracias.
Jason

1

Mi solución a esto es:


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


Esta es una solución sucia. En su vista parcial, debería poder verificar si hay un Modelo nulo, en lugar de verificar si la lista tiene algún valor y si es nulo.
madd
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.