3 回答

TA貢獻(xiàn)1821條經(jīng)驗(yàn) 獲得超6個(gè)贊
快速的答案是使用for()循環(huán)代替foreach()循環(huán)。就像是:
@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)
}
}
}
但這掩蓋了為什么它可以解決問題。
在解決此問題之前,您至少應(yīng)該對(duì)三件事有所了解。我不得不承認(rèn),我貨物culted這個(gè)很長(zhǎng)一段時(shí)間,當(dāng)我開始與框架的工作。我花了相當(dāng)長(zhǎng)時(shí)間才真正了解正在發(fā)生的事情。
這三件事是:
如何在LabelFor和其他...For助手在MVC工作?
什么是表情樹?
Model Binder如何工作?
所有這三個(gè)概念鏈接在一起以獲得答案。
如何在LabelFor和其他...For助手在MVC工作?
所以,你已經(jīng)使用了HtmlHelper<T>用于擴(kuò)展LabelFor和TextBoxFor與其他人,你可能已經(jīng)注意到了,當(dāng)你調(diào)用它們,你通過他們的λ,它神奇地產(chǎn)生一些HTML。但是如何?
因此,首先要注意的是這些助手的簽名。讓我們看一下最簡(jiǎn)單的重載 TextBoxFor
public static MvcHtmlString TextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
首先,這是HtmlHelpertype 的強(qiáng)類型的擴(kuò)展方法<TModel>。因此,為了簡(jiǎn)單說明幕后發(fā)生的事情,當(dāng)剃刀渲染此視圖時(shí),它將生成一個(gè)類。該類的內(nèi)部是HtmlHelper<TModel>(作為屬性Html,這就是為什么可以使用@Html...)的實(shí)例,其中TModel是@model語(yǔ)句中定義的類型。因此,在您的情況下,當(dāng)您查看此視圖時(shí),TModel 將始終為類型ViewModels.MyViewModels.Theme。
現(xiàn)在,下一個(gè)參數(shù)有些棘手。因此,讓我們看一下調(diào)用
@Html.TextBoxFor(model=>model.SomeProperty);
似乎我們有點(diǎn)lambda,如果要猜測(cè)簽名,則可能會(huì)認(rèn)為此參數(shù)的類型只是a Func<TModel, TProperty>,其中TModelview模型TProperty 的類型被推斷為屬性的類型。
但這并不完全正確,如果您查看參數(shù)的實(shí)際類型Expression<Func<TModel, TProperty>>。
因此,當(dāng)您通常生成lambda時(shí),編譯器會(huì)將該lambda并將其編譯為MSIL,就像其他任何函數(shù)一樣(這就是為什么您可以或多或少可互換地使用委托,方法組和lambda的原因,因?yàn)樗鼈冎皇谴a引用) )
但是,當(dāng)編譯器看到類型為時(shí)Expression<>,它不會(huì)立即將lambda編譯為MSIL,而是生成一個(gè)Expression Tree!
什么是表情樹?
因此,到底是一個(gè)表達(dá)式樹。好吧,這并不復(fù)雜,但也不是在公園散步。引用ms:
| 表達(dá)式樹表示樹狀數(shù)據(jù)結(jié)構(gòu)中的代碼,其中每個(gè)節(jié)點(diǎn)都是表達(dá)式,例如,方法調(diào)用或諸如x <y的二進(jìn)制運(yùn)算。
簡(jiǎn)而言之,表達(dá)式樹是功能作為“動(dòng)作”集合的表示。
在的情況下model=>model.SomeProperty,表達(dá)式樹中將有一個(gè)節(jié)點(diǎn),上面寫著:“從“模型”中獲取“某些屬性””
可以將表達(dá)式樹編譯成可以調(diào)用的函數(shù),但是只要是表達(dá)式樹,它就是節(jié)點(diǎn)的集合。
那有什么好處呢?
所以Func<>或Action<>,一旦有了它們,它們幾乎是原子的。您真正能做的就是Invoke()他們,也就是告訴他們?nèi)プ鏊麄儜?yīng)該做的工作。
Expression<Func<>>另一方面,表示動(dòng)作的集合,可以將其附加,操縱,訪問或編譯和調(diào)用。
那你為什么要告訴我這一切?
因此,有了對(duì)“什么Expression<>是”的理解,我們可以回到Html.TextBoxFor。呈現(xiàn)文本框時(shí),它需要生成一些有關(guān)您為其提供的屬性的信息。喜歡的東西attributes對(duì)物業(yè)進(jìn)行驗(yàn)證,特別在這種情況下,需要弄清楚如何命名的<input>標(biāo)簽。
它通過“遍歷”表達(dá)式樹并建立名稱來實(shí)現(xiàn)。因此,對(duì)于像這樣的表達(dá)式model=>model.SomeProperty,它將遍歷該表達(dá)式,以收集您要的屬性并進(jìn)行構(gòu)建<input name='SomeProperty'>。
對(duì)于更復(fù)雜的示例,例如model=>model.Foo.Bar.Baz.FooBar,它可能會(huì)生成<input name="Foo.Bar.Baz.FooBar" value="[whatever FooBar is]" />
說得通?在這里,不僅要做的是工作Func<>,而且如何完成工作也很重要。
(請(qǐng)注意,諸如LINQ to SQL之類的其他框架通過遍歷表達(dá)式樹并構(gòu)建不同的語(yǔ)法(在這種情況下為SQL查詢)來執(zhí)行類似的操作)
Model Binder如何工作?
因此,一旦您了解了這一點(diǎn),我們就必須簡(jiǎn)要討論一下模型綁定器。發(fā)布表單時(shí),就像是一個(gè)flat Dictionary<string, string>,我們失去了嵌套視圖模型可能具有的層次結(jié)構(gòu)。模型綁定器的工作是采用此鍵值對(duì)組合并嘗試為具有某些屬性的對(duì)象補(bǔ)水。它是如何做到的?您使用發(fā)布的輸入的“鍵”或名稱來猜對(duì)了。
因此,如果表單發(fā)布看起來像
Foo.Bar.Baz.FooBar = Hello
然后,您將發(fā)布到名為的模型SomeViewModel,那么它的作用與助手最初所做的相反。它尋找一個(gè)名為“ Foo”的屬性。然后從“ Foo”中尋找一個(gè)名為“ Bar”的屬性,然后從“ Baz”中尋找...等等。
最后,它嘗試將值解析為“ FooBar”的類型,并將其分配給“ FooBar”。
EW!
瞧,您有您的模型。剛構(gòu)造的Model Binder實(shí)例將移至請(qǐng)求的Action中。
因此您的解決方案不起作用,因?yàn)镠tml.[Type]For()助手需要表達(dá)。而您只是給他們一個(gè)價(jià)值。它不知道該值的上下文是什么,也不知道如何處理它。
現(xiàn)在有人建議使用局部視圖進(jìn)行渲染?,F(xiàn)在,理論上這將起作用,但可能不會(huì)達(dá)到您期望的方式。渲染局部圖像時(shí),您將更改的類型TModel,因?yàn)槟幱诓煌囊晥D上下文中。這意味著您可以用較短的表達(dá)式描述屬性。這也意味著當(dāng)助手為您的表達(dá)式生成名稱時(shí),它會(huì)很淺。它只會(huì)基于給定的表達(dá)式生成(而不是整個(gè)上下文)。
因此,可以說您有一個(gè)部分僅渲染了“ Baz”(來自之前的示例)。在該部分中,您只能說:
@Html.TextBoxFor(model=>model.FooBar)
而不是
@Html.TextBoxFor(model=>model.Foo.Bar.Baz.FooBar)
這意味著它將生成一個(gè)如下所示的輸入標(biāo)簽:
<input name="FooBar" />
其中,如果您將此表單發(fā)布到期望有一個(gè)很大的深嵌套ViewModel的動(dòng)作中,則它將嘗試合并名為FooBaroff的屬性TModel。充其量是沒有的,而充其量是最糟糕的。如果您要發(fā)布到接受Baz而不是根模型的特定操作,那么效果很好!實(shí)際上,局部視圖是更改視圖上下文的好方法,例如,如果您的頁(yè)面具有多種形式,并且所有頁(yè)面都發(fā)布到不同的動(dòng)作,那么為每個(gè)頁(yè)面呈現(xiàn)局部視圖將是一個(gè)好主意。
現(xiàn)在,一旦您掌握了所有這些內(nèi)容,就可以開始使用進(jìn)行真正有趣的事情Expression<>,方法是對(duì)它們進(jìn)行編程擴(kuò)展,并使用它們進(jìn)行其他巧妙的工作。我什么都不會(huì)做。但是,希望這可以使您更好地了解幕后發(fā)生的事情以及事物按原樣運(yùn)行的原因。
- 3 回答
- 0 關(guān)注
- 270 瀏覽
添加回答
舉報(bào)