renderpartial with null model gets passed the wrong type

asp.net MvcRenderpartial

asp.net Mvc Problem Overview


I have a page:

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

And on it, the following:

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

Here is the DTO object:

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

and here is the partial:

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

When Model.Tasks is not null, everything works fine. However when its null I get:

> The model item passed into the dictionary is of type > 'DTOSearchResults' but this dictionary requires a model item of type > 'System.Collections.Generic.IEnumerable`1[Task]'.

I figured it must not know which overload to use, so I did this (see below) to be explicit, but I still get the same issue!

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

I know I can work around this by checking for null, or not even passing null, but that's not the point. Why is this happening?

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

Andrew I think the problem you are getting is a result of the RenderPartial method using the calling (view)'s model to the partial view when the model you pass is null.. you can get around this odd behavior by doing:

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

Does that help?

Solution 2 - asp.net Mvc

@myandmycode's answer is good, but a slightly shorter one would be

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

This works because the ViewDataDictionary is the thing that holds the model, and it can accept a model as a constructor parameter. This basically passes an "entire" view data dictionary, which of course only contains the possibly-null model.

Solution 3 - asp.net Mvc

It appears that when the property of the Model you're passing in is null MVC intentionally reverts back to the "parent" Model. Apparently the MVC engine interprets a null model value as intent to use the previous one.

Slightly more details here: https://stackoverflow.com/questions/1049027/asp-net-mvc-strongly-typed-views-partial-view-parameters-glitch

Solution 4 - asp.net Mvc

If you do not want to loose your previous ViewData in the partial view, you could try:

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

Solution 5 - asp.net Mvc

A solution would be to create a HtmlHelper like this:

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

The Partial<T>(...) matched before the Partial(...) so convenient and no ambiguity error when compiling.

Personally I find it difficult to understand the behaviour - seems hard to imagine this as design choice?

Solution 6 - asp.net Mvc

Though this has been answered, I ran across this and decided I wanted to solve this issue for my project instead of working around it with new ViewDataDictionary().

I created a set of extension methods: https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
I also added some methods that don't call the partial if the model is null, this will save a lot of if statements.

I created them for Razor, but a couple of them should also work with aspx style views (the ones that use HelperResult probably aren't compatible).

The extension methods look like this:

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

There are also methods for IEnumerable<object> models and the discard ones can also be called with a Razor lambda that allow you to wrap the partial result with some html.

Feel free to use them if you like.

Solution 7 - asp.net Mvc

My workaround to this is:


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


Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrew BullockView Question on Stackoverflow
Solution 1 - asp.net MvcmeandmycodeView Answer on Stackoverflow
Solution 2 - asp.net MvcconfiguratorView Answer on Stackoverflow
Solution 3 - asp.net MvcZackView Answer on Stackoverflow
Solution 4 - asp.net MvcFran PView Answer on Stackoverflow
Solution 5 - asp.net MvcColin BreameView Answer on Stackoverflow
Solution 6 - asp.net MvcJaapView Answer on Stackoverflow
Solution 7 - asp.net Mvch3nView Answer on Stackoverflow