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?

Andrew Bullock
fuente

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?

meandmycode
fuente
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.

configurador
fuente
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

Zack
fuente
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});%>
Fran P
fuente
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?

Colin Breame
fuente
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.

Jaap
fuente
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()); %>

h3n
fuente
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