简体   繁体   中英

Passing null child object from parent object to a partial view

I have an object which contains models for my ASP.NET MVC web app. The Model that is being passed into the view has sub models for "gadgets" on that particular view. Each of these sub models gets passed to a partial view (gadget).

The problem is when I have a null model in the view model. See example below.

View Model:

public class FooBarHolder()
{
     public FooBar1 FooBar1 { get; set; }
     public FooBar2 FooBar2 { get; set; }
}

We pass FooBarHolder into the view and inside the view we make calls such as

<% Html.RenderPartial("Foo", Model.FooBar1); %>
<% Html.RenderPartial("Foo2", Model.FooBar2); %>

Now say for instance that Model.FooBar2 was null. What I am experiencing from the strongly typed partial view is an error that says "This view expected a model of type FooBar2 but got a model of type FooBarHolder."

Why is this happening instead of just passing in a null?

That's how the RenderPartial method works (I know should have been documented, blogged about, etc..., me too I find this a little strange). If you do not specify a model or pass null it will use the model of the parent page. To avoid this you might use the null coalescing operator:

<% Html.RenderPartial("Foo", Model.FooBar1 ?? new Foo()); %>

And if you are really curious as to how this is implemented there's an excerpt from the relevant parts of the ASP.NET MVC 2 source code:

// Renders the partial view with an empty view data and the given model
public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model) {
    htmlHelper.RenderPartialInternal(partialViewName, htmlHelper.ViewData, model, htmlHelper.ViewContext.Writer, ViewEngines.Engines);
}

internal virtual void RenderPartialInternal(string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer, ViewEngineCollection viewEngineCollection) {
    if (String.IsNullOrEmpty(partialViewName)) {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName");
    }

    ViewDataDictionary newViewData = null;

    if (model == null) {
        if (viewData == null) {
            newViewData = new ViewDataDictionary(ViewData);
        }
        else {
            newViewData = new ViewDataDictionary(viewData);
        }
    }
    else {
        if (viewData == null) {
            newViewData = new ViewDataDictionary(model);
        }
        else {
            newViewData = new ViewDataDictionary(viewData) { Model = model };
        }
    }

    ViewContext newViewContext = new ViewContext(ViewContext, ViewContext.View, newViewData, ViewContext.TempData, writer);
    IView view = FindPartialView(newViewContext, partialViewName, viewEngineCollection);
    view.Render(newViewContext, writer);
}

Notice how the case of null model is handled.

To avoid passing parent's model when child model is null, use this trick:

@Html.Partial("Child", null, new ViewDataDictionary<ChildType>(childInstance/*this can be null*/))

Credit where due...

My workaround for this strange "feature" (or bug maybe?) is:

<% Html.RenderPartial(
    "Foo2", 
    new ViewDataDictionary(ViewData) { Model = Model.FooBar2 }
); %>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM