200
votes

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?

7

7 Answers

351
votes

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?

48
votes

@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.

27
votes

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: ASP.NET MVC, strongly typed views, partial view parameters glitch

20
votes

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});%>
12
votes

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?

11
votes

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.

1
votes

My workaround to this is:


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