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

Елемент моделі, переданий у словник, має тип "DTOSearchResults", але для цього словника потрібен елемент моделі типу "System.Collections.Generic.IEnumerable`1 [Завдання]".

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

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

Я знаю, що я можу обійти це, перевіривши на null або навіть не передавши null, але це не в тому. Чому це відбувається?

Відповіді:


349

Ендрю. Я думаю, що проблема, яку ви отримуєте, є результатом методу RenderPartial, використовуючи модель виклику (перегляду) для часткового перегляду, коли модель, яку ви передаєте, є нульовою. Ви можете обійти цю дивну поведінку, зробивши:

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

Чи допомагає це?


16
Ще економить час людям. Я витягнув волосся над цим.
Джеймс Григорій

3
Я розумію, чому вони підтримують нульову модель і передають сторінки Модель, але не могли вони це впоратися, перевантажуючи їх. @ Html.Render ("ослів") відрізняється від @ Html.Render ("ослів", canbenull)
Phil Strong

19
Я вважаю це дуже протизаконним, тому я додав "питання", проголосуйте за нього, якщо ви згодні: aspnet.codeplex.com/workitem/8872
pbz

3
Я виявив, що з цим рішенням мій ValidationSummary в моєму частковому перегляді не працює, оскільки ViewData первинної моделі втрачено в частковому представленні. Я використовував відповідь, надану тут, stackoverflow.com/a/12037580/649497 для вирішення цього питання.
BruceHill

5
Вам слід пройти уздовж існуючої ViewData: новий ViewDataDictionary (ViewData)
ScottE

48

@ myandmycode відповідь хороша, але трохи коротша була б

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

Це працює, тому що ViewDataDictionaryсаме те, що містить модель, і може приймати модель як параметр конструктора. Це в основному передає "весь" словник даних перегляду, який, звичайно, містить лише можливу нульову модель.


2
@jcmcbeth: Ем, ні, це не так ... Я точно використав цей точний код із нулями.
конфігуратор

1
@jcmcbeth: Ви використовуєте new ViewDataDictionary(null)? Тому що це дозволило б вибрати іншу перевантаження, ViewDataDictionaryпараметр, який, ймовірно, не приймає нулі.
конфігуратор

1
Здається, що використання властивості ViewBag викликає виклик неправильного конструктора. Те, як він приймає динамічний тип і припускає, що це ViewDataDictionary над об’єктом, для мене не має сенсу, але, здається, це те, що він робить. Вам доведеться кинути його об’єкту, щоб він обрав правильний конструктор.
Joel McBeth

1
@jcmcbeth: Виклик його за динамічним типом використовує те саме, як якщо б ви дали фактичне значення; якщо значення є null, це те саме, що викликати, new ViewDataDictionary(null)що викликає виклик найбільш конкретної перевантаження.
конфігуратор

1
якщо ви використовуєте його так, сумнівна помилка зникла .. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks))Ви використовуєте неправильний конструктор, якщо його немає.
Філіп Корнеліссен

26

Здається, що коли властивість моделі, яку ви передаєте, є нульовою, MVC навмисно повертається назад до "батьківської" моделі. Мабуть, двигун MVC інтерпретує нульове значення моделі як намір використовувати попереднє.

Трохи більше деталей тут: ASP.NET MVC, сильно набрані подання, збої параметрів часткового перегляду


1
+1 за те, що насправді намагаються пояснити проблему, а не просто трактують це як дивну поведінку
YavgenyP

Так, це сталося зі мною, і вищезгадане не виправило, воно просто дало мені трохи більше інформації про мою фактичну помилку.
Canvas

20

Якщо ви не хочете втратити попередні ViewData в частковому поданні, ви можете спробувати:

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

1
Схоже, це не відповідає на питання.
Джон Сондерс

6
+1 Насправді це працює. Це в основному та сама ідея, представлена ​​тут stackoverflow.com/a/713921/649497, але долає проблему з цією відповіддю, і це те, що ViewData зникне, якщо інстанціювати ViewDataDictionary з порожнім конструктором. Я спершу вирішив цю проблему прийнятим рішенням, а потім виявив, що мій ValidationSummary не працює в частковому режимі. Це рішення вирішило це для мене. Ця відповідь потребує більшого визнання для вирішення проблеми та збереження ViewData у вашому частковому перегляді.
BruceHill

1
@Franc P це фактично спрацювало, не втрачаючи значень ViewBag, і, отже, пройшло нульову модель. Дякую.
Закер

Це правильна відповідь, якщо вам потрібен доступ до ViewBag у ваших партіях!
Даніель Лоренц

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
Ендрю Буллок

Хороший, але не зрозумілий для користувача. Скажімо, я звик до того, що використовує моя колегія у своєму проекті, я починаю свіжий. Тоді зовсім забудьте додати цю перевантаження і вуаля, винятки починають траплятися у виробництві, оскільки ми не перевірили її досить добре. Інша назва - beter imho.
Яап

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()); %>


Це брудне рішення. На частковому перегляді ви повинні мати можливість перевірити нульову модель, а не перевіряти, чи є у списку якісь значення та чи немає.
божевільний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.