renderpartial с нулевой моделью передается неправильный тип


у меня есть страницы:

<%@ 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>>" %>

Когда Модель.Задачи не нулевые, все работает нормально. Однако если его значение null, я получаю:

элемент модели, переданный в словарь, имеет тип 'DTOSearchResults', но этот словарь требует элементов модели типа - Система.Коллекции.Родовой.IEnumerable`1[Задача]'.

я решил, что он не должен знать, какую перегрузку использовать, поэтому я сделал это (см. ниже), чтобы быть явным, но я все равно получаю ту же проблему!

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

Я знаю, что могу обойти это, проверяя null или даже не передавая null, но это не главное. Почему это происходит?

7 189

7 ответов:

Эндрю я думаю, что проблема, которую вы получаете, является результатом метода RenderPartial, использующего модель вызова (представления) для частичного представления, когда модель, которую вы передаете, равна нулю.. вы можете обойти это странное поведение, делая:

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

это поможет?

@myandmycode ответ хороший, но немного короче будет

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

это работает, потому что ViewDataDictionary - это то, что содержит модель, и она может принимать модель в качестве параметра конструктора. Это в основном передает" весь " словарь данных представления, который, конечно же, содержит только возможную нулевую модель.

похоже, что когда свойство модели, которую вы передаете, равно null, MVC намеренно возвращается к "родительской" модели. По-видимому, механизм MVC интерпретирует нулевое значение модели как намерение использовать предыдущее.

чуть подробнее здесь:ASP.NET MVC, строго типизированные представления, частичные параметры просмотра Глюк

Если вы не хотите потерять свои предыдущие ViewData в частичном представлении, вы можете попробовать:

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

решение было бы создать 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);
}

The Partial<T>(...) совпадали до Partial(...) так удобно и нет ошибки двусмысленности при компиляции.

лично мне трудно понять поведение - трудно представить это как выбор дизайна?

хотя на это был дан ответ, я столкнулся с этим и решил, что хочу решить эту проблему для своего проекта вместо того, чтобы обойти ее с new ViewDataDictionary().

Я создал набор методов расширения : https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
Я также добавил некоторые методы, которые не вызывают частичное, если модель равна нулю, это сэкономит много операторов if.

Я создал их для бритвы, но некоторые из них также должны работать с представлениями стиля 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> модели и отбрасывать те, которые также могут быть вызваны с бритвой лямбда, которые позволяют обернуть частичный результат с некоторым html.

не стесняйтесь использовать их, если хотите.

мой обходной путь для этого:


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