คำตอบอย่างรวดเร็วคือการใช้for()ห่วงในสถานที่ของคุณforeach()ลูป สิ่งที่ต้องการ:
@for(var themeIndex = 0; themeIndex < Model.Theme.Count(); themeIndex++)
{
   @Html.LabelFor(model => model.Theme[themeIndex])
   @for(var productIndex=0; productIndex < Model.Theme[themeIndex].Products.Count(); productIndex++)
   {
      @Html.LabelFor(model=>model.Theme[themeIndex].Products[productIndex].name)
      @for(var orderIndex=0; orderIndex < Model.Theme[themeIndex].Products[productIndex].Orders; orderIndex++)
      {
          @Html.TextBoxFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].Quantity)
          @Html.TextAreaFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].Note)
          @Html.EditorFor(model => model.Theme[themeIndex].Products[productIndex].Orders[orderIndex].DateRequestedDeliveryFor)
      }
   }
}
แต่สิ่งนี้ทำให้เข้าใจได้ว่าเหตุใดจึงแก้ไขปัญหาได้
มีสามสิ่งที่คุณมีความเข้าใจคร่าวๆก่อนที่จะแก้ไขปัญหานี้ได้ ฉันต้องยอมรับว่าฉันได้รับลัทธินี้มานานแล้วเมื่อฉันเริ่มทำงานกับเฟรมเวิร์ก และใช้เวลาพอสมควรในการทำความเข้าใจกับสิ่งที่เกิดขึ้น
ทั้งสามสิ่งคือ:
- วิธีทำ
LabelForและอื่น ๆ ที่...Forผู้ช่วยทำงานใน MVC? 
- Expression Tree คืออะไร?
 
- Model Binder ทำงานอย่างไร?
 
แนวคิดทั้งสามนี้เชื่อมโยงกันเพื่อให้ได้คำตอบ
วิธีทำLabelForและอื่น ๆ ที่...Forผู้ช่วยทำงานใน MVC?
ดังนั้นคุณได้ใช้HtmlHelper<T>ส่วนขยายสำหรับLabelForและTextBoxForและอื่น ๆ และคุณอาจสังเกตเห็นว่าเมื่อคุณเรียกใช้พวกเขาคุณส่งแลมบ์ดาและสร้าง html ขึ้นมาอย่างน่าอัศจรรย์ แต่อย่างไร?
ดังนั้นสิ่งแรกที่ต้องสังเกตคือลายเซ็นสำหรับตัวช่วยเหล่านี้ มาดูการโอเวอร์โหลดที่ง่ายที่สุดสำหรับ
TextBoxFor
public static MvcHtmlString TextBoxFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression
) 
ขั้นแรกนี่เป็นวิธีการขยายสำหรับHtmlHelperประเภทที่พิมพ์<TModel>มากเกินไป ดังนั้นเพื่อระบุสิ่งที่เกิดขึ้นเบื้องหลังเมื่อมีดโกนแสดงมุมมองนี้มันจะสร้างคลาส ภายในคลาสนี้เป็นอินสแตนซ์ของHtmlHelper<TModel>(เป็นคุณสมบัติHtmlซึ่งเป็นเหตุผลที่คุณสามารถใช้ได้@Html...) TModelประเภทที่กำหนดไว้ใน@modelคำสั่งของคุณอยู่ที่ไหน ดังนั้นในกรณีของคุณเมื่อคุณดูมุมมองนี้TModel
จะเป็นประเภทViewModels.MyViewModels.Themeเสมอ
ตอนนี้การโต้เถียงครั้งต่อไปค่อนข้างยุ่งยาก ดังนั้นมาดูคำวิงวอน
@Html.TextBoxFor(model=>model.SomeProperty);
ดูเหมือนว่าเรามีแลมด้าตัวเล็ก ๆ และถ้ามีใครจะเดาลายเซ็นเราอาจคิดว่าประเภทของอาร์กิวเมนต์นี้จะเป็น a ประเภทของโมเดลมุมมองFunc<TModel, TProperty>อยู่ที่ไหนTModelและTProperty
อนุมานเป็นประเภทของคุณสมบัติ
แต่ thats ไม่ถูกต้องถ้าคุณมองไปที่เกิดขึ้นจริงExpression<Func<TModel, TProperty>>ประเภทของการโต้แย้งของมัน
ดังนั้นเมื่อคุณสร้างแลมบ์ดาตามปกติคอมไพลเลอร์จะนำแลมด้าและรวบรวมลงใน MSIL เช่นเดียวกับฟังก์ชันอื่น ๆ (ซึ่งเป็นเหตุผลที่คุณสามารถใช้ตัวแทนกลุ่มวิธีการและแลมบดาแทนกันได้มากหรือน้อยเพราะเป็นเพียงการอ้างอิงโค้ด .)
อย่างไรก็ตามเมื่อคอมไพเลอร์เห็นว่า type เป็น an Expression<>มันจะไม่รวบรวม lambda ลงไปที่ MSIL ในทันที แต่มันจะสร้าง Expression Tree ขึ้นมาแทน!
ดังนั้นสิ่งที่ห่าคือต้นไม้แสดงออก ไม่ซับซ้อน แต่ก็ไม่ได้เดินเล่นในสวนสาธารณะเช่นกัน ในการอ้างถึง MS:
| แผนภูมินิพจน์แทนรหัสในโครงสร้างข้อมูลแบบต้นไม้โดยที่แต่ละโหนดเป็นนิพจน์ตัวอย่างเช่นการเรียกใช้เมธอดหรือการดำเนินการไบนารีเช่น x <y
พูดง่ายๆก็คือต้นไม้นิพจน์คือการแสดงฟังก์ชันเป็นชุดของ "การกระทำ"
ในกรณีของmodel=>model.SomePropertyโครงสร้างนิพจน์จะมีโหนดอยู่ในนั้นซึ่งระบุว่า: "Get 'Some Property' from a 'model'"
แผนภูมินิพจน์นี้สามารถรวบรวมเป็นฟังก์ชันที่สามารถเรียกใช้งานได้ แต่ตราบใดที่เป็นโครงสร้างนิพจน์จะเป็นเพียงชุดของโหนด
แล้วมันดีสำหรับอะไร?
ดังนั้นFunc<>หรือAction<>เมื่อคุณมีแล้วพวกมันก็เป็นปรมาณู สิ่งที่คุณทำได้ก็คือInvoke()พวกเขาหรือที่เรียกว่าบอกให้พวกเขาทำงานที่ควรจะทำ
Expression<Func<>>ในทางกลับกันแสดงถึงชุดของการกระทำซึ่งสามารถต่อท้ายจัดการเยี่ยมชมหรือรวบรวมและเรียกใช้
ทำไมคุณถึงบอกฉันทั้งหมดนี้?
ดังนั้นด้วยความเข้าใจว่าExpression<>คืออะไรเราสามารถย้อนกลับไปHtml.TextBoxForได้ เมื่อแสดงผลกล่องข้อความจำเป็นต้องสร้างบางสิ่งเกี่ยวกับคุณสมบัติที่คุณมอบให้ สิ่งที่ต้องการattributesในสถานที่สำหรับการตรวจสอบและโดยเฉพาะในกรณีนี้จะต้องคิดออกว่าจะตั้งชื่อ<input>แท็ก
ทำได้โดยการ "เดิน" ต้นไม้นิพจน์และสร้างชื่อ ดังนั้นสำหรับการแสดงออกเช่นนั้นก็เดินแสดงออกรวบรวมคุณสมบัติที่คุณจะขอและสร้างmodel=>model.SomeProperty<input name='SomeProperty'>
สำหรับตัวอย่างที่ซับซ้อนมากขึ้นเช่นmodel=>model.Foo.Bar.Baz.FooBarอาจสร้าง<input name="Foo.Bar.Baz.FooBar" value="[whatever FooBar is]" />
เข้าท่า? มันไม่ได้เป็นเพียงการทำงานว่าFunc<>ไม่ แต่วิธีการมันไม่ทำงานมันเป็นสิ่งสำคัญที่นี่
(โปรดสังเกตว่ากรอบงานอื่น ๆ เช่น LINQ ถึง SQL ทำสิ่งที่คล้ายกันโดยการเดินแผนภูมินิพจน์และสร้างไวยากรณ์ที่แตกต่างกันซึ่งในกรณีนี้คือแบบสอบถาม SQL)
Model Binder ทำงานอย่างไร?
ดังนั้นเมื่อคุณได้รับสิ่งนั้นเราต้องพูดคุยสั้น ๆ เกี่ยวกับตัวยึดแบบจำลอง เมื่อโพสต์แบบฟอร์มก็เหมือนกับแบน
 Dictionary<string, string>เราได้สูญเสียโครงสร้างลำดับชั้นที่โมเดลมุมมองซ้อนกันของเราอาจมี เป็นงานของตัวประสานแบบจำลองที่จะใช้คำสั่งผสมคู่คีย์ - ค่านี้และพยายามทำให้วัตถุที่มีคุณสมบัติบางอย่างกลับคืนมา มันทำอย่างไร? คุณเดาได้โดยใช้ "คีย์" หรือชื่อของอินพุตที่โพสต์
ดังนั้นหากโพสต์แบบฟอร์มดูเหมือน
Foo.Bar.Baz.FooBar = Hello
และคุณกำลังโพสต์ไปยังโมเดลที่เรียกว่าSomeViewModelจากนั้นมันจะกลับกันสิ่งที่ผู้ช่วยเหลือทำในตอนแรก มันมองหาอสังหาริมทรัพย์ที่เรียกว่า "Foo" จากนั้นมองหาอสังหาริมทรัพย์ที่เรียกว่า "บาร์" จาก "Foo" จากนั้นมองหา "Baz" ... และอื่น ๆ ...
ในที่สุดก็พยายามแยกวิเคราะห์ค่าเป็นประเภท "FooBar" และกำหนดให้ "FooBar" 
วุ้ย!!!
และ voila คุณมีแบบจำลองของคุณ อินสแตนซ์ Model Binder ที่เพิ่งสร้างจะถูกส่งไปยัง Action ที่ร้องขอ
ดังนั้นวิธีแก้ปัญหาของคุณจึงไม่ได้ผลเนื่องจากHtml.[Type]For()ผู้ช่วยเหลือต้องการการแสดงออก และคุณแค่ให้คุณค่าแก่พวกเขา ไม่มีความคิดว่าบริบทสำหรับค่านั้นคืออะไรและไม่รู้ว่าจะทำอย่างไรกับมัน
ตอนนี้มีบางคนแนะนำให้ใช้บางส่วนเพื่อแสดงผล ตอนนี้ในทางทฤษฎีจะได้ผล แต่อาจไม่ใช่วิธีที่คุณคาดหวัง เมื่อคุณแสดงผลบางส่วนคุณกำลังเปลี่ยนประเภทTModelเนื่องจากคุณอยู่ในบริบทมุมมองที่แตกต่างกัน ซึ่งหมายความว่าคุณสามารถอธิบายคุณสมบัติของคุณด้วยสำนวนสั้น ๆ นอกจากนี้ยังหมายความว่าเมื่อผู้ช่วยเหลือสร้างชื่อสำหรับการแสดงออกของคุณมันจะตื้น จะสร้างตามนิพจน์ที่กำหนดเท่านั้น (ไม่ใช่บริบททั้งหมด)
สมมติว่าคุณมีบางส่วนที่แสดงเพียง "Baz" (จากตัวอย่างก่อนหน้านี้) ภายในบางส่วนคุณสามารถพูดได้ว่า:
@Html.TextBoxFor(model=>model.FooBar)
ค่อนข้างมากกว่า
@Html.TextBoxFor(model=>model.Foo.Bar.Baz.FooBar)
นั่นหมายความว่าจะสร้างแท็กอินพุตดังนี้:
<input name="FooBar" />
ซึ่งถ้าคุณกำลังโพสต์แบบฟอร์มนี้เพื่อการดำเนินการที่คาดว่าจะมีขนาดใหญ่ซ้อนกันอย่างลึกซึ้ง ViewModel แล้วก็จะพยายามให้ความชุ่มชื้นคุณสมบัติที่เรียกว่าออกจากFooBar TModelสิ่งที่ดีที่สุดไม่มีอยู่ที่นั่นและที่แย่ที่สุดคืออย่างอื่นทั้งหมด หากคุณกำลังโพสต์ไปยังการดำเนินการเฉพาะที่ยอมรับ a Bazแทนที่จะเป็นรูปแบบรูทสิ่งนี้จะได้ผลดี! ในความเป็นจริงบางส่วนเป็นวิธีที่ดีในการเปลี่ยนบริบทการดูของคุณเช่นหากคุณมีเพจที่มีหลายรูปแบบที่โพสต์ไปยังการกระทำที่แตกต่างกันการแสดงผลบางส่วนสำหรับแต่ละส่วนจะเป็นความคิดที่ดี
เมื่อคุณได้สิ่งเหล่านี้ทั้งหมดแล้วคุณสามารถเริ่มทำสิ่งที่น่าสนใจได้ด้วยการExpression<>ขยายโปรแกรมและทำสิ่งอื่น ๆ ที่เป็นระเบียบ ฉันจะไม่เข้าใจอะไรเลย แต่หวังว่านี่จะช่วยให้คุณเข้าใจสิ่งที่เกิดขึ้นเบื้องหลังได้ดีขึ้นและเหตุใดสิ่งต่างๆจึงดำเนินไปอย่างที่เป็นอยู่
     
              
@ก่อนทั้งหมดforeach? คุณไม่ควรมี lambdas ในHtml.EditorFor(Html.EditorFor(m => m.Note)เช่น) และวิธีการอื่น ๆ หรือไม่? ฉันอาจเข้าใจผิด แต่คุณช่วยวางรหัสจริงของคุณได้ไหม ฉันค่อนข้างใหม่กับ MVC แต่คุณสามารถแก้ไขได้ค่อนข้างง่ายด้วยมุมมองบางส่วนหรือบรรณาธิการ (ถ้าเป็นชื่อ?)