renderpartial dengan model null akan melewati tipe yang salah

198

Saya punya halaman:

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

Dan di atasnya, berikut ini:

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

Ini adalah objek DTO:

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

dan di sini adalah parsial:

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

Ketika Model.Tasks tidak nol, semuanya berfungsi dengan baik. Namun ketika nolnya saya dapatkan:

Item model yang diteruskan ke kamus adalah tipe 'DTOSearchResults' tetapi kamus ini membutuhkan item model tipe 'System.Collections.Generic.IEnumerable`1 [Task]'.

Saya pikir itu pasti tidak tahu kelebihan yang digunakan, jadi saya melakukan ini (lihat di bawah) secara eksplisit, tapi saya masih mendapatkan masalah yang sama!

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

Saya tahu saya bisa mengatasi ini dengan memeriksa nol, atau bahkan tidak melewati nol, tapi bukan itu intinya. Mengapa ini terjadi?

Andrew Bullock
sumber

Jawaban:

349

Andrew Saya pikir masalah yang Anda dapatkan adalah hasil dari metode RenderPartial menggunakan model panggilan (tampilan) ke tampilan parsial ketika model yang Anda lewati adalah nol .. Anda dapat mengatasi perilaku aneh ini dengan melakukan:

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

Apakah itu membantu?

meandmycode
sumber
16
Masih menghemat waktu orang. Saya mencabut rambut saya karena ini.
James Gregory
3
Saya mengerti mengapa mereka mendukung model nol dan melewati Model halaman tetapi mereka tidak bisa mengatasinya dengan overloading. @ Html.Render ("keledai") berbeda dari @ Html.Render ("keledai", canbenull)
Phil Strong
19
Saya menemukan ini sangat berlawanan dengan intuisi sehingga saya menambahkan "masalah", suara di atasnya jika Anda setuju: aspnet.codeplex.com/workitem/8872
PBZ
3
Saya menemukan bahwa dengan solusi ini ValidationSummary saya dalam tampilan parsial saya tidak berfungsi karena ViewData model utama hilang dalam tampilan parsial. Saya menggunakan jawaban yang diberikan di sini stackoverflow.com/a/12037580/649497 untuk mengatasi ini.
BruceHill
5
Anda harus menyampaikan sepanjang ViewData yang ada: ViewDataDictionary baru (ViewData)
ScottE
48

@ myandmycode jawabannya bagus, tapi jawaban yang sedikit lebih pendek

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

Ini berfungsi karena ViewDataDictionaryhal yang memegang model, dan dapat menerima model sebagai parameter konstruktor. Ini pada dasarnya melewati kamus data tampilan "keseluruhan", yang tentu saja hanya berisi model yang mungkin-nol.

konfigurator
sumber
2
@ jcmcbeth: Erm, tidak, tidak ... Saya sudah berhasil menggunakan kode ini dengan nulls.
konfigurator
1
@ jcmcbeth: Apakah Anda menggunakan new ViewDataDictionary(null)? Karena itu akan memilih kelebihan yang berbeda, satu dengan ViewDataDictionaryparameter, yang mungkin tidak akan menerima nol.
konfigurator
1
Tampaknya menggunakan properti ViewBag menyebabkan konstruktor yang salah dipanggil. Bagaimana cara mengambil jenis dinamis dan menganggap itu adalah ViewDataDictionary lebih dari satu objek tidak masuk akal bagi saya, tetapi tampaknya apa yang dilakukannya. Anda harus melemparkannya ke objek untuk memilih konstruktor yang benar.
Joel McBeth
1
@ jcmcbeth: Memanggilnya menggunakan tipe dinamis menggunakan sama seperti jika Anda telah memberikan nilai aktual; jika nilainya null, itu sama dengan panggilan new ViewDataDictionary(null)yang menyebabkan overload paling spesifik dipanggil.
konfigurator
1
jika Anda menggunakannya seperti ini, kesalahan diksi produk susu hilang .. Html.RenderPartial("TaskList", new ViewDataDictionary(model: Model.Tasks))Anda menggunakan konstruktor yang salah jika itu nol.
Filip Cornelissen
26

Tampaknya ketika properti Model yang Anda lewati adalah null MVC dengan sengaja kembali ke Model "induk". Rupanya mesin MVC menginterpretasikan nilai model nol sebagai maksud untuk menggunakan yang sebelumnya.

Lebih detail sedikit di sini: ASP.NET MVC, tampilan sangat diketik, kesalahan parameter tampilan sebagian

Zack
sumber
1
+1 untuk benar-benar mencoba menjelaskan masalah ini, dan tidak hanya memperlakukan ini sebagai perilaku aneh
YavgenyP
Yap ini terjadi pada saya dan di atas tidak memperbaikinya, itu hanya memberi saya informasi lebih banyak tentang kesalahan saya yang sebenarnya.
Kanvas
20

Jika Anda tidak ingin kehilangan ViewData Anda sebelumnya dalam tampilan parsial, Anda dapat mencoba:

<% Html.RenderPartial("TaskList", Model.Tasks, new ViewDataDictionary(ViewData){Model = null});%>
Fran P
sumber
1
Ini sepertinya tidak menjawab pertanyaan.
John Saunders
6
+1 Sebenarnya ini berfungsi. Ini pada dasarnya ide yang sama disajikan di sini stackoverflow.com/a/713921/649497 tetapi mengatasi masalah dengan jawaban itu dan itu adalah bahwa ViewData akan hilang jika Anda instantiate ViewDataDictionary dengan konstruktor kosong. Saya pertama kali memecahkan masalah ini dengan solusi yang diterima dan kemudian menemukan bahwa ValidationSummary saya tidak berfungsi dalam tampilan sebagian. Solusi ini memecahkannya untuk saya. Jawaban ini membutuhkan lebih banyak pengakuan untuk menyelesaikan masalah dan mempertahankan ViewData di sebagian tampilan Anda.
BruceHill
1
@ Franc P ini benar-benar bekerja tanpa kehilangan nilai-nilai ViewBag dan karenanya melewati model nol. Terima kasih.
Zaker
Ini adalah jawaban yang tepat jika Anda membutuhkan akses ViewBag di Partial Anda!
Daniel Lorenz
12

Sebuah solusi adalah membuat HtmlHelper seperti ini:

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>(...)cocok sebelum Partial(...)sangat nyaman dan tidak ada kesalahan ambiguitas saat kompilasi.

Secara pribadi saya merasa sulit untuk memahami perilaku - sepertinya sulit membayangkan ini sebagai pilihan desain?

Colin Breame
sumber
1
inilah yang saya lakukan pada akhirnya. tidak ada banyak pilihan desain / perilaku di asp.net mvc apa yang masuk akal. sejak ditinggalkan itu. membantu orang lain, jadi miliki +1
Andrew Bullock
Bagus, namun tidak jelas bagi pengguna. Katakanlah saya sudah terbiasa dengan apa yang digunakan rekan saya dalam proyeknya, saya memulai yang baru. Kemudian benar-benar lupa menambahkan kelebihan ini dan voilla, pengecualian mulai terjadi dalam produksi karena kami tidak mengujinya dengan cukup baik. Nama yang berbeda adalah beter imho.
Jaap
11

Meskipun ini telah dijawab, saya menemukan ini dan memutuskan saya ingin menyelesaikan masalah ini untuk proyek saya daripada menyelesaikannya new ViewDataDictionary() .

Saya membuat serangkaian metode ekstensi: https://github.com/q42jaap/PartialMagic.Mvc/blob/master/PartialMagic.Mvc/PartialExtensions.cs
Saya juga menambahkan beberapa metode yang tidak memanggil parsial jika modelnya nol , ini akan menghemat banyak pernyataan if.

Saya membuat mereka untuk Razor, tetapi beberapa dari mereka juga harus bekerja dengan tampilan gaya aspx (yang menggunakan HelperResult mungkin tidak kompatibel).

Metode ekstensi terlihat seperti ini:

@* calls the partial with Model = null *@
@Html.PartialOrNull("PartialName", null)
@* does not call the partial if the model is null *@
@Html.PartialOrDiscard("PartialName", null)

Ada juga metode untuk IEnumerable<object>model dan yang dibuang juga dapat disebut dengan lambda Razor yang memungkinkan Anda untuk membungkus sebagian hasil dengan beberapa html.

Silakan menggunakannya jika Anda mau.

Jaap
sumber
1
Masih bermanfaat pada MVC5: 6/25/2014. Terima kasih.
Jason
1

Solusi saya untuk ini adalah:


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

h3n
sumber
Ini solusi kotor. Pada tampilan parsial Anda, Anda harus dapat memeriksa Model nol, daripada memeriksa apakah daftar memiliki nilai dan apakah itu nol.
madd