使用空模型的renderpartial会传递错误类型

203

我有一个页面:

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

上面的内容如下:

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

这里是 DTO 对象:

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

这里是部分内容:

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

当Model.Tasks不为空时,一切正常。但是当它为空时,我会得到以下错误:

传递给字典的模型项的类型为'DTOSearchResults',但该字典需要一个类型为'System.Collections.Generic.IEnumerable`1[Task]'的模型项。

我想这是因为它不知道要使用哪个重载,所以我显式地进行了如下操作,但是我仍然遇到相同的问题!

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

我知道我可以通过检查null或者不传递null来解决这个问题,但那不是重点。为什么会发生这种情况?

7个回答

355

Andrew,我认为你遇到的问题是RenderPartial方法在传入的模型为空时会使用调用(视图)的模型来呈现该局部视图。你可以通过以下方式解决这个奇怪的行为:

@{ Html.RenderPartial("_partialViewName", new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "prefixHere" } }); }
<% Html.RenderPartial("TaskList", Model.Tasks, new ViewDataDictionary()); %>

这有帮助吗?


16
仍然在为节省人们的时间而努力。这让我抓狂了。 - James Gregory
3
我理解他们支持空模型和传递页面模型的原因,但他们是否可以通过重载来处理。@Html.Render("donkeys") 与 @Html.Render("donkeys", couldbenull) 是不同的。 - Phil Strong
19
我觉得这很不符合直觉,所以我添加了一个“问题”,如果您同意,请投票:http://aspnet.codeplex.com/workitem/8872。 - pbz
3
我发现使用这个解决方案后,我的部分视图里的ValidationSummary无法正常工作,因为在部分视图中丢失了主模型的ViewData。我使用了这里给出的答案https://dev59.com/KXRB5IYBdhLWcg3wWGEH#12037580 来解决这个问题。 - BruceHill
5
你应该传递现有的ViewData:new ViewDataDictionary(ViewData)。 - ScottE
显示剩余9条评论

48

@myandmycode的答案很好,但稍微简短一些的答案是

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

这样能够工作,是因为ViewDataDictionary是保存模型的东西,它可以接受一个模型作为构造函数参数。这基本上传递了一个“完整”的视图数据字典,其中当然只包含可能为null的模型。


将空值传递给ViewDataDictionary构造函数将引发ArgumentNullException异常。 - Joel McBeth
2
@jcmcbeth:额,不,它并没有……我已经成功地使用了这个完全相同的代码来处理null值。 - configurator
1
@jcmcbeth:你是否在使用 new ViewDataDictionary(null)?因为这会选择另一个重载,其中包含一个 ViewDataDictionary 参数,该参数可能不接受 null 值。 - configurator
1
似乎使用ViewBag属性会导致调用错误的构造函数。它将动态类型视为ViewDataDictionary而不是对象,这对我来说没有意义,但它似乎就是这样做的。您需要将其强制转换为对象才能选择正确的构造函数。 - Joel McBeth
1
@jcmcbeth:如果使用动态类型调用它,则与提供实际值相同;如果该值为null,则与调用new ViewDataDictionary(null)相同,这会导致调用最具体的重载。 - configurator
2
如果您像这样使用,字典错误就会消失.. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks)) 如果它是空的,那么您正在使用错误的构造函数。 - Filip Cornelissen

29

1
+1 是为了表扬你真正尝试解释问题,而不是将其视为奇怪的行为。 - YavgenyP
是的,这也发生在我身上,上面提到的方法并没有解决问题,只是给了我更多有关实际错误的信息。 - Canvas

21

如果您不想在部分视图中丢失先前的ViewData,可以尝试:

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

1
这似乎并没有回答问题。 - John Saunders
7
+1 实际上它是起作用的。它基本上是在这里提出的相同想法https://dev59.com/KXRB5IYBdhLWcg3wWGEH#713921,但克服了那个答案的一个问题,即如果您使用空构造函数实例化ViewDataDictionary,则ViewData将会丢失。我最初用被接受的解决方案解决了这个问题,然后发现我的ValidationSummary在局部视图中无法工作。这个解决方案解决了我的问题。这个答案需要更多的认可,因为它解决了问题并在您的局部视图中保留了ViewData。 - BruceHill
1
@Franc P,这实际上可以在不丢失ViewBag值的情况下工作,并因此传递了一个空模型。谢谢。 - Zaker
如果您需要在局部视图中访问ViewBag,则这是正确的答案! - Daniel Lorenz

12
一种解决方法是创建一个像这样的HtmlHelper: ```

创建如下HtmlHelper:

```
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);
}
Partial<T>(...)Partial(...)更匹配,因此在编译时方便且没有模糊错误。就我个人而言,我觉得很难理解这种行为 - 似乎很难将其作为设计选择?

1
最终我做的是放弃了ASP.NET MVC,因为它没有太多有意义的设计选择和行为。虽然对其他人可能有帮助,所以我给它点个赞。 - Andrew Bullock
很不错,但是对用户来说不够清晰。假设我习惯于使用同事在他的项目中使用的东西,然后我开始一个新的项目。然后完全忘记添加这个重载,结果异常在生产环境中开始发生,因为我们没有好好测试它。在我看来,换个名字会更好。 - Jaap
一些工具将无法使用此扩展方法链接到视图。 - Myster

11
尽管这个问题已经得到了解答,但我遇到了这个问题,并决定为我的项目解决这个问题,而不是用new ViewDataDictionary()来绕过它。
我创建了一组扩展方法:https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs。 我还添加了一些方法,如果模型为空,则不调用partial,这将节省很多if语句。
我为Razor创建了它们,但其中一些也应该适用于aspx样式的视图(那些使用HelperResult的可能不兼容)。
这些扩展方法看起来像这样:
@* calls the partial with Model = null *@
@Html.PartialOrNull("PartialName", null)
@* does not call the partial if the model is null *@
@Html.PartialOrDiscard("PartialName", null)

还有一些针对 IEnumerable<object> 模型的方法,可以使用 Razor lambda 来调用丢弃方法,并允许您将部分结果包装在某些 HTML 中。

如果您喜欢,请随意使用它们。


1
截至2014年6月25日,仍然适用于MVC5。谢谢。 - Jason

1

我解决这个问题的方法是:


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


这是一个不太优雅的解决方案。在你的部分视图中,你应该检查空的 Model,而不是检查列表是否有任何值和是否为空。 - madd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接