ASP.Net MVC Html.HiddenFor dengan nilai yang salah

132

Saya menggunakan MVC 3 dalam proyek saya, dan saya melihat perilaku yang sangat aneh.

Saya mencoba membuat bidang tersembunyi untuk nilai tertentu pada Model saya, masalahnya adalah karena alasan tertentu nilai yang ditetapkan pada bidang tersebut tidak sesuai dengan nilai dalam Model.

misalnya

Saya memiliki kode ini, hanya sebagai ujian:

<%:Html.Hidden("Step2", Model.Step) %>
<%:Html.HiddenFor(m => m.Step) %>

Saya akan berpikir bahwa kedua bidang tersembunyi akan memiliki nilai yang sama. Apa yang saya lakukan adalah, tetapkan nilai ke 1 saat pertama kali saya menampilkan tampilan, dan kemudian setelah pengiriman saya menambah nilai bidang Model oleh 1.

Jadi, pertama kali saya membuat halaman kedua kontrol memiliki nilai 1, tetapi kedua kalinya nilai yang diberikan adalah ini:

<input id="Step2" name="Step2" type="hidden" value="2" />
<input id="Step" name="Step" type="hidden" value="1" />

Seperti yang Anda lihat, nilai pertama sudah benar, tetapi nilai kedua tampaknya sama dengan pertama kali saya menampilkan tampilan.

Apa yang saya lewatkan? Apakah * Untuk bantuan Html caching nilai dalam beberapa cara? Jika demikian, bagaimana saya bisa menonaktifkan caching ini?

Terima kasih atas bantuan Anda.

willvv
sumber
Saya baru saja menguji sesuatu yang lain. Jika saya menghapus panggilan HiddenFor dan hanya membiarkan panggilan Tersembunyi, tetapi menggunakan nama "Langkah", itu juga membuat hanya nilai pertama (1).
willvv
1
terjadi di dapatkan juga
Oren A

Jawaban:

191

Itu normal dan begitulah cara kerja pembantu HTML. Mereka pertama-tama menggunakan nilai permintaan POST dan setelah itu nilai dalam model. Ini berarti bahwa bahkan jika Anda mengubah nilai model dalam aksi controller Anda jika ada variabel yang sama dalam permintaan POST modifikasi Anda akan diabaikan dan nilai POSTed akan digunakan.

Salah satu solusi yang mungkin adalah menghapus nilai ini dari keadaan model dalam tindakan pengontrol yang mencoba mengubah nilai:

// remove the Step variable from the model state 
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

Kemungkinan lain adalah menulis bantuan HTML kustom yang akan selalu menggunakan nilai model dan mengabaikan nilai POST.

Dan kemungkinan lain:

<input type="hidden" name="Step" value="<%: Model.Step %>" />
Darin Dimitrov
sumber
5
Saya sangat menghargai posting blog Simon Ince tentang ini. Kesimpulan yang saya ambil darinya adalah untuk memastikan alur kerja Anda benar. Jadi, jika Anda telah menerima model tampilan yang valid dan melakukan sesuatu dengannya maka arahkan ke tindakan konfirmasi, bahkan jika ini juga hanya retreives dan menampilkan model yang setara. Ini berarti Anda memiliki ModelState baru. blogs.msdn.com/b/simonince/archive/2010/05/05/… (ditautkan dari posting yang saya tulis hari ini: oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html )
Lisa
2
Saya sangat suka MVC3, tetapi bit ini benar-benar kikuk. Saya harap mereka memperbaikinya di MVC4.
KennyZ
5
Wow, yang satu ini membuat saya pergi cukup lama. Saya pada dasarnya menggunakan saran pertama tetapi baru saja memanggil ModelState.Clear () sebelum kembali. Ini sepertinya berhasil, apakah ada alasan untuk tidak menggunakan Clear?
Jason
1
".Hapus" tidak berfungsi untuk saya. Tapi ModelState.Clear () melakukannya tepat sebelum kembali di Controller. Menulis kustom Tersembunyi Anda juga akan berfungsi dengan baik. Ini semua terjadi karena pengembang tidak ingin kehilangan "nilai formulir" mereka jika mereka menekan "kirim" dan DB tidak menyimpan dengan benar. Solusi terbaik: jangan beri nama bidang berbeda pada halaman yang sama dengan nama / id yang sama.
Dexter
1
FYI perilaku menjengkelkan ini dengan ramah dibawa ke ASP.NET Core kalau-kalau ada yang khawatir hal-hal akan menjadi lebih baik
John Hargrove
19

Saya mengalami masalah yang sama ketika menulis Wizard yang menunjukkan bagian berbeda dari model yang lebih besar di setiap langkah.
Data dan / atau Kesalahan dari "Langkah 1" akan bercampur dengan "Langkah 2", dll, sampai akhirnya saya menyadari bahwa ModelState yang harus disalahkan.

Ini adalah solusi sederhana saya:

if (oldPageIndex != newPageIndex)
{
    ModelState.Clear(); // <-- solution
}

return View(model[newPageIndex]);
Peter B
sumber
10
ModelState.Clear()memecahkan masalah saya dengan permintaan POST berurutan dalam situasi yang sama.
Evan Mulawski
Terima kasih atas tip ModelState.Clear () Evan. Ini adalah anomali yang belum pernah saya temui sebelumnya. Saya memiliki beberapa posting ajax.beginform berurutan dan salah satunya mempertahankan nilai dari posting sebelumnya. Lubang hitam debugging. Adakah yang tahu mengapa ini di-cache?
Rob
1

Kode ini tidak akan berfungsi

// remove the Step variable from the model state
// if you want the changes in the model to be
// taken into account
ModelState.Remove("Step");
model.Step = 2;

... karena HiddenFor always (!) membaca dari ModelState bukan model itu sendiri. Dan jika tidak menemukan kunci "Langkah" itu akan menghasilkan default untuk tipe variabel yang akan menjadi 0 dalam kasus ini

Ini solusinya. Saya menulisnya untuk diri saya sendiri tetapi tidak keberatan membagikannya karena saya melihat banyak orang berjuang dengan pembantu HiddenFor yang nakal ini.

public static class CustomExtensions
{
    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
    {
        ReplacePropertyState(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression, htmlAttributes);
    }

    private static void ReplacePropertyState<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        string text = ExpressionHelper.GetExpressionText(expression);
        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text);
        ModelStateDictionary modelState = htmlHelper.ViewContext.ViewData.ModelState;
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

        if (modelState.ContainsKey(fullName))
        {                
            ValueProviderResult currentValue = modelState[fullName].Value;
            modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture);
        }
        else
        {
            modelState[fullName] = new ModelState
            {
                Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), CultureInfo.CurrentUICulture)
            };
        }
    }
}

Maka Anda cukup menggunakannya seperti biasa dari dalam tampilan Anda:

@Html.HiddenFor2(m => m.Id)

Perlu disebutkan itu berfungsi dengan koleksi juga.

Ruslan Georgievskiy
sumber
solusi ini tidak sepenuhnya berfungsi. Setelah posting kembali properti berikutnya adalah null dalam Aksi
user576510
Nah, ini adalah kode dari produksi tempat ia bekerja dengan baik. Saya tidak tahu mengapa itu tidak berhasil untuk Anda, tetapi jika Anda melihat bidang tersembunyi dengan nilai yang benar diberikan pada halaman saya tidak melihat alasan yang jelas mengapa itu tidak akan dikembalikan ke properti model. Jika Anda melihat nilai bidang tersembunyi salah pada halaman - itu cerita lain, saya akan sangat ingin tahu dalam keadaan apa ini terjadi sebelum hal yang sama terjadi pada produksi saya :-) Terima kasih.
Ruslan Georgievskiy
0

Saya terlalu berjuang dengan situasi yang sama saya pikir, di mana saya menggunakan model yang sama antara panggilan, dan ketika saya mengubah properti model di backend. Padahal, itu tidak masalah bagi saya, jika saya menggunakan textboxfor atau hiddenfor.

Saya hanya memotong situasi dengan menggunakan skrip halaman untuk menyimpan nilai model sebagai variabel js, karena saya memerlukan hiddenfield untuk tujuan itu pada awalnya.

Tidak yakin apakah ini membantu tetapi pertimbangkan saja ..

abdulkadir pekeroglu
sumber