ASP.Net MVC Html.HiddenFor ที่มีค่าผิด


132

ฉันใช้ MVC 3 ในโครงการของฉันและฉันเห็นพฤติกรรมแปลก ๆ

ฉันกำลังพยายามสร้างฟิลด์ที่ซ่อนอยู่สำหรับค่าเฉพาะในโมเดลของฉันปัญหาคือด้วยเหตุผลบางประการค่าที่ตั้งบนฟิลด์ไม่สอดคล้องกับค่าในโมเดล

เช่น

ฉันมีรหัสนี้เช่นเดียวกับการทดสอบ:

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

ฉันคิดว่าฟิลด์ที่ซ่อนทั้งสองจะมีค่าเท่ากัน สิ่งที่ฉันทำคือตั้งค่าเป็น 1 ในครั้งแรกที่ฉันแสดงมุมมองจากนั้นหลังจากการส่งฉันจะเพิ่มค่าของฟิลด์ Model ขึ้น 1

ดังนั้นครั้งแรกที่ฉันแสดงผลหน้าทั้งสองตัวควบคุมมีค่า 1 แต่ครั้งที่สองค่าที่แสดงผลคือ:

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

อย่างที่คุณเห็นค่าแรกถูกต้อง แต่ค่าที่สองดูเหมือนจะเหมือนกับครั้งแรกที่ฉันแสดงมุมมอง

ฉันขาดอะไรไป? ผู้ช่วย * สำหรับ Html กำลังแคชค่าในทางใดทางหนึ่งหรือไม่? ถ้าเป็นเช่นนั้นฉันจะปิดใช้งานการแคชนี้ได้อย่างไร?

ขอบคุณสำหรับความช่วยเหลือของคุณ.


ฉันเพิ่งทดสอบอย่างอื่น ถ้าฉันลบการเรียก HiddenFor และปล่อยให้เฉพาะการโทรที่ซ่อนอยู่ แต่ใช้ชื่อ "ขั้นตอน" มันจะแสดงผลเฉพาะค่าแรก (1)
willvv

1
เกิดขึ้นเช่นกัน
Oren A

คำตอบ:


191

นั่นเป็นเรื่องปกติและเป็นวิธีการทำงานของตัวช่วย HTML พวกเขาใช้ค่าของคำขอ POST ก่อนและหลังจากนั้นค่าในแบบจำลอง ซึ่งหมายความว่าแม้ว่าคุณจะแก้ไขค่าของโมเดลในแอ็คชันคอนโทรลเลอร์ของคุณหากมีตัวแปรเดียวกันในคำขอ POST การแก้ไขของคุณจะถูกละเว้นและจะใช้ค่า POSTed

วิธีแก้ปัญหาที่เป็นไปได้อย่างหนึ่งคือการลบค่านี้ออกจากสถานะแบบจำลองในการดำเนินการของคอนโทรลเลอร์ซึ่งพยายามแก้ไขค่า:

// 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;

ความเป็นไปได้อีกประการหนึ่งคือการเขียนตัวช่วย HTML ที่กำหนดเองซึ่งจะใช้ค่าของโมเดลและละเว้นค่า POST

และความเป็นไปได้อื่น:

<input type="hidden" name="Step" value="<%: Model.Step %>" />

5
ฉันชื่นชมบล็อกโพสต์ของ Simon Ince เกี่ยวกับเรื่องนี้มาก ข้อสรุปที่ฉันใช้คือเพื่อให้แน่ใจว่าเวิร์กโฟลว์ของคุณถูกต้อง ดังนั้นหากคุณยอมรับโมเดลมุมมองที่ถูกต้องและดำเนินการบางอย่างแล้วให้เปลี่ยนเส้นทางไปยังการดำเนินการยืนยันแม้ว่าจะเป็นการดึงกลับและแสดงโมเดลที่เทียบเท่าก็ตาม ซึ่งหมายความว่าคุณมี ModelState ใหม่ blogs.msdn.com/b/simonince/archive/2010/05/05/… (เชื่อมโยงจากโพสต์ที่ฉันเขียนในวันนี้: oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html )
Lisa

2
ฉันชอบ MVC3 มาก แต่บิตนี้เป็นเรื่องที่น่าเบื่อจริงๆ ฉันหวังว่าพวกเขาจะแก้ไขได้ใน MVC4
KennyZ

6
ว้าวคนนี้ให้ฉันไปพักหนึ่งแล้ว โดยทั่วไปฉันใช้คำแนะนำแรก แต่เรียกว่า ModelState.Clear () ก่อนกลับ ดูเหมือนจะใช้งานได้ดีมีเหตุผลใดที่จะไม่ใช้ Clear หรือไม่?
Jason

1
".Remove" ไม่ได้ผลสำหรับฉัน แต่ ModelState.Clear () ทำก่อนที่จะกลับมาใน Controller การเขียน Hidden ของคุณเองก็ใช้ได้ดีเช่นกัน ทั้งหมดนี้เกิดขึ้นเนื่องจากนักพัฒนาไม่ต้องการสูญเสีย "ค่าฟอร์ม" หากกด "ส่ง" และฐานข้อมูลไม่ได้บันทึกอย่างถูกต้อง ทางออกที่ดีที่สุด: อย่าตั้งชื่อช่องต่างๆในหน้าเดียวกันด้วยชื่อ / รหัสเดียวกัน
Dexter

2
FYI พฤติกรรมที่น่ารำคาญนี้ถูกนำไปยัง ASP.NET Core ในกรณีที่ใครก็ตามกังวลว่าสิ่งต่างๆจะดีขึ้น
John Hargrove

19

ฉันพบปัญหาเดียวกันเมื่อเขียน Wizard ที่แสดงส่วนต่างๆของโมเดลขนาดใหญ่ในทุกขั้นตอน
ข้อมูลและ / หรือข้อผิดพลาดจาก "ขั้นตอนที่ 1" จะปะปนกับ "ขั้นตอนที่ 2" ฯลฯ จนในที่สุดฉันก็รู้ว่า ModelState คือ "ตำหนิ"

นี่เป็นวิธีง่ายๆของฉัน:

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

return View(model[newPageIndex]);

10
ModelState.Clear()แก้ไขปัญหาของฉันเกี่ยวกับคำขอ POST ตามลำดับในสถานการณ์ที่คล้ายคลึงกัน
Evan Mulawski

ขอบคุณสำหรับเคล็ดลับ ModelState.Clear () Evan นี่เป็นความผิดปกติที่ฉันไม่เคยพบมาก่อน ฉันมีโพสต์ ajax.beginform ตามลำดับหลายรายการและหนึ่งในนั้นยังคงรักษาค่าจากโพสต์ก่อนหน้านี้ การแก้จุดบกพร่องของหลุมดำ ใครรู้ว่าทำไมถึงถูกแคช?
Rob

1

รหัสนี้จะไม่ทำงาน

// 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;

... เนื่องจาก HiddenFor always (!) อ่านจาก ModelState ไม่ใช่ตัวแบบ และหากไม่พบคีย์ "ขั้นตอน" ระบบจะสร้างค่าเริ่มต้นสำหรับประเภทตัวแปรนั้นซึ่งจะเป็น 0 ในกรณีนี้

นี่คือวิธีแก้ปัญหา ฉันเขียนมันเพื่อตัวเอง แต่ไม่รังเกียจที่จะแบ่งปันเพราะฉันเห็นหลายคนกำลังดิ้นรนกับตัวช่วย HiddenFor ที่ซุกซนนี้

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)
            };
        }
    }
}

จากนั้นคุณก็ใช้งานได้ตามปกติจากภายในคุณดู:

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

มันคุ้มที่จะพูดถึงมันใช้งานได้กับคอลเลกชันด้วย


โซลูชันนี้ทำงานได้ไม่เต็มที่ หลังจากโพสต์ถัดไปคุณสมบัติกลับเป็นโมฆะในการดำเนินการ
user576510

นี่คือรหัสจากการผลิตที่ใช้งานได้ดี ฉันไม่สามารถบอกได้ว่าทำไมมันถึงไม่ได้ผลสำหรับคุณ แต่ถ้าคุณเห็นฟิลด์ที่ซ่อนอยู่พร้อมกับค่าที่ถูกต้องแสดงบนเพจฉันไม่เห็นเหตุผลที่ชัดเจนว่าทำไมมันถึงไม่ถูกเรียกคืนในคุณสมบัติของโมเดล หากคุณเห็นค่าฟิลด์ที่ซ่อนไม่ถูกต้องบนหน้านั่นเป็นอีกเรื่องหนึ่งฉันอยากรู้มากว่าเหตุการณ์นี้เกิดขึ้นภายใต้สถานการณ์ใดก่อนที่สิ่งเดียวกันจะเกิดขึ้นในการผลิตของฉัน :-) ขอบคุณ
Ruslan Georgievskiy

0

ฉันกำลังดิ้นรนกับสถานการณ์เดียวกันมากเกินไปที่ฉันใช้สถานะแบบจำลองเดียวกันระหว่างการโทรและเมื่อฉันแก้ไขคุณสมบัติของโมเดลในแบ็กเอนด์ แม้ว่าจะไม่สำคัญสำหรับฉันถ้าฉันใช้ textboxfor หรือ hiddenfor

ฉันแค่ข้ามสถานการณ์โดยใช้สคริปต์เพจเพื่อเก็บค่าโมเดลเป็นตัวแปร js เพราะฉันต้องการฟิลด์ที่ซ่อนไว้สำหรับจุดประสงค์นั้นในตอนเริ่มต้น

ไม่แน่ใจว่านี่ช่วยได้ไหม แต่แค่พิจารณา ..

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.