-->

ASP.NET MVC, strongly typed views, partial view pa

2019-01-14 16:00发布

问题:

If i got view which inherits from:

System.Web.Mvc.ViewPage<Foo>

Where Foo has a property Bar with a type string
And view wants to render strongly typed partial view which inherits from:

System.Web.Mvc.ViewUserControl<string>  

like this:

Html.RenderPartial("_Bar", Model.Bar);%>

Then why it will throw this:

The model item passed into the dictionary is of type 'Foo'
but this dictionary requires a model item of type 'System.String'.

when bar is not initialized?

More specific: why it passes Foo, where it should pass null?

回答1:

As @Dennis points out, if the model value is null, it will use the existing model from the view. The reason for this is to support the ability to call a partial view using a signature that contains only the partial view name and have it reuse the existing model. Internally, all of the RenderPartial helpers defer to a single RenderPartialInternal method. The way you get that method to reuse the existing model is to pass in a null value for the model (which the signature that takes only a view name does). When you pass a null value to the signature containing both a view name and a model object, you are essentially replicating the behavior of the method that takes only the view name.

This should fix your issue:

<% Html.RenderPartial( "_Bar", Model.Bar ?? string.Empty ) %>


回答2:

Look at ASP.NET MVC source (HtmlHelper.cs -> RenderPartialInternal method -> line 258):

...

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

...

this is exactly your case. ASP.NET MVC uses the ViewData from your ViewContext

UPDATED:

Try this instead:

<% Html.RenderPartial("_Bar", Model.Bar ?? "Default" ); %>


回答3:

If you pass null as the model to RenderPartial, then it will look at the original model, which is why the error says foo.

You'll need to make sure that bar is initialized to be an empty string instead of null.

Edit: @Arnis, look at the source code. It doesn't lie. You are passing null to the overload of RenderPartial. You are not passing Foo. Internally, the system uses the Model from your page's ViewContext (which is Foo) when you pass a null Bar to RenderPartial.



回答4:

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