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

198

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

<%@ 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 не имеет значения null, все работает нормально. Однако, когда его ноль, я получаю:

Элемент модели, передаваемый в словарь, имеет тип 'DTOSearchResults', но для этого словаря требуется элемент модели типа 'System.Collections.Generic.IEnumerable`1 [Task]'.

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

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

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

Эндрю Баллок
источник

Ответы:

349

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

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

Это помогает?

meandmycode
источник
16
Все еще экономит время людей. Я тянул волосы за это.
Джеймс Грегори
3
Я понимаю, почему они поддерживают нулевую модель и пропускают страницы Model, но они не могли справиться с этим путем перегрузки. @ Html.Render («ослы») отличается от @ Html.Render («ослы», canbenull)
Фил Стронг,
19
Я нахожу это очень нелогичным, поэтому я добавил «проблему», проголосуйте, если вы согласны: aspnet.codeplex.com/workitem/8872
pbz
3
Я обнаружил, что с этим решением моя ValidationSummary в моем частичном представлении не работает, потому что ViewData основной модели был потерян в частичном представлении. Я использовал ответ, данный здесь stackoverflow.com/a/12037580/649497, чтобы решить эту проблему.
БрюсХилл
5
Вы должны передать существующие ViewData: новый ViewDataDictionary (ViewData)
Скотт
48

Ответ @ myandmycode хорош, но немного короче

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

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

конфигуратор
источник
2
@jcmcbeth: Хм, нет, это не так ... Я успешно использовал этот точный код с нулями.
конфигуратор
1
@jcmcbeth: вы используете new ViewDataDictionary(null)? Потому что это выберет другую перегрузку с ViewDataDictionaryпараметром, который, вероятно, не будет принимать значения NULL.
конфигуратор
1
Может показаться, что использование свойства ViewBag вызывает неправильный конструктор. То, как он принимает динамический тип и предполагает, что это ViewDataDictionary над объектом, не имеет смысла для меня, но, похоже, это то, что он делает. Вам нужно будет привести его к объекту, чтобы он выбрал правильный конструктор.
Джоэл МакБет
1
@jcmcbeth: при вызове динамического типа используется то же самое, как если бы вы указали фактическое значение; если значение равно null, это то же самое, что вызов, new ViewDataDictionary(null)который вызывает наиболее специфическую перегрузку.
конфигуратор
1
если вы используете его так, ошибка словаря исчезнет. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks))Вы используете неправильный конструктор, если он нулевой.
Филип Корнелиссен
26

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

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

Zack
источник
1
+1 за то, что на самом деле пытался объяснить проблему, а не просто
воспринимать
Да, это происходило со мной, и вышеупомянутое не исправило это, это только дало мне немного больше информации о моей фактической ошибке.
Холст
20

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

<% Html.RenderPartial("TaskList", Model.Tasks, new ViewDataDictionary(ViewData){Model = null});%>
Фрэн П
источник
1
Это, кажется, не отвечает на вопрос.
Джон Сондерс
6
+1 На самом деле это работает. Это в основном та же самая идея, представленная здесь stackoverflow.com/a/713921/649497, но она преодолевает проблему с этим ответом и заключается в том, что ViewData пропадет, если вы создадите экземпляр ViewDataDictionary с пустым конструктором. Сначала я решил эту проблему с помощью принятого решения, а затем обнаружил, что мой ValidationSummary не работает в частичном представлении. Это решение решило это для меня. Этот ответ требует большего признания для решения проблемы и сохранения ViewData в вашем частичном представлении.
БрюсХилл
1
@Franc P это на самом деле работало без потери значений ViewBag и, следовательно, прошло нулевую модель. Спасибо.
Закер
Это правильный ответ, если вам нужен доступ к ViewBag в ваших Partials!
Даниэль Лоренц
12

Решением было бы создать 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 не так много вариантов дизайна / поведения, которые имеют какой-либо смысл. так как отказался от него. полезно для других, но имейте +1
Эндрю Баллок
Хороший, но неясный для пользователя. Допустим, я привык к тому, что мой коллега использует в своем проекте, я начинаю новый. Тогда полностью забудьте добавить эту перегрузку и вуаля, исключения начинают происходить в производстве, потому что мы не проверили это достаточно хорошо. Другое имя - Беттер Имхо.
Яап
11

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

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

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

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

Яап
источник
1
По-прежнему полезно с MVC5: 25.06.2014. Спасибо.
Джейсон
1

Мой обходной путь к этому:


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

H3N
источник
Это грязное решение. При частичном просмотре вы должны иметь возможность проверять нулевую модель, а не проверять, есть ли в списке какие-либо значения и имеет ли он нулевое значение.
MADD