Hızlı cevap, döngülerinizin for()
yerine bir döngü kullanmaktır foreach()
. Gibi bir şey:
@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)
}
}
}
Ancak bu, bunun neden sorunu çözdüğünü açıklıyor .
Bu sorunu çözmeden önce en azından üstünkörü bir anlayışa sahip olduğunuz üç şey var. Bunu çerçeve ile çalışmaya başladığımda uzun bir süre kargo kültivasyonu yaptığımı itiraf etmeliyim . Ve gerçekten neler olup bittiğini anlamam epey zaman aldı.
Bu üç şey:
- MVC'de
LabelFor
ve diğer ...For
yardımcılar nasıl çalışır?
- İfade Ağacı nedir?
- Model Bağlayıcı nasıl çalışır?
Bu kavramların üçü de bir cevap almak için birbirine bağlanır.
MVC'de LabelFor
ve diğer ...For
yardımcılar nasıl çalışır?
Yani, kullandığınız HtmlHelper<T>
için uzantıları LabelFor
ve TextBoxFor
ve diğerleri, ve muhtemelen bunları çağırmak, onlara bir lambda geçmek ve ne zaman fark sihirli bazı html üretir. Ama nasıl?
Yani ilk fark edilecek şey bu yardımcıların imzasıdır. Şunun için en basit aşırı yüklemeye bakalım
TextBoxFor
public static MvcHtmlString TextBoxFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
Birincisi, bu, HtmlHelper
türü güçlü bir şekilde belirlenmiş bir tür için bir genişletme yöntemidir <TModel>
. Yani, perde arkasında ne olduğunu basitçe ifade etmek gerekirse, ustura bu görüşü ortaya koyduğunda bir sınıf oluşturur. Bu sınıfın İçinde bir örneğidir HtmlHelper<TModel>
(özelliği olarak Html
kullanabilirsiniz yüzden, @Html...
,) TModel
sizin tanımlanan türüdür @model
açıklamada. Yani sizin durumunuzda, bu görüşe baktığınızda TModel
her zaman tipte olacaktır ViewModels.MyViewModels.Theme
.
Şimdi, bir sonraki argüman biraz aldatıcı. Öyleyse bir çağrıya bakalım
@Html.TextBoxFor(model=>model.SomeProperty);
Görünüşe göre küçük bir lambda varmış gibi görünüyor ve eğer biri imzayı tahmin edecekse, bu argümanın türünün basitçe a olacağını düşünebilir Func<TModel, TProperty>
, burada TModel
görünüm modelinin TProperty
türü ve özelliğin türü olarak çıkarılır.
Ancak argümanın gerçek türüne bakarsanız, bu pek doğru değil Expression<Func<TModel, TProperty>>
.
Bu nedenle, normalde bir lambda oluşturduğunuzda, derleyici lambda'yı alır ve diğer işlevler gibi MSIL'de derler (bu nedenle, temsilciler, yöntem grupları ve lambdaları birbirinin yerine daha çok veya daha az kullanabilirsiniz, çünkü bunlar yalnızca kod referanslarıdır. .)
Ancak, derleyici türün bir an olduğunu gördüğünde, Expression<>
lambda'yı hemen MSIL'e derlemez, bunun yerine bir İfade Ağacı oluşturur!
Öyleyse, lanet olası bir ifade ağacıdır. Pekala, karmaşık değil ama parkta bir yürüyüş de değil. Ms alıntı yapmak için:
| İfade ağaçları, ağaç benzeri bir veri yapısındaki kodu temsil eder; burada her düğüm bir ifade, örneğin bir yöntem çağrısı veya x <y gibi bir ikili işlem.
Basitçe ifade etmek gerekirse, bir ifade ağacı, bir işlevin "eylemler" koleksiyonu olarak temsilidir.
Bu durumda model=>model.SomeProperty
, ifade ağacının içinde "Modelden 'Bazı Özellik Al" yazan bir düğüm bulunur.
Bu ifade ağacı, çağrılabilen bir işlev olarak derlenebilir , ancak bir ifade ağacı olduğu sürece, yalnızca bir düğümler koleksiyonudur.
Peki bu ne için iyi?
Yani Func<>
ya Action<>
da onlara sahip olduğunuzda, hemen hemen atomiktirler. Gerçekten yapabileceğiniz tek şey Invoke()
onlar, yani yapmaları gereken işi yapmalarını söylemek.
Expression<Func<>>
diğer yandan eklenebilen, manipüle edilebilen, ziyaret edilebilen veya derlenip çağrılabilen bir eylemler koleksiyonunu temsil eder .
Öyleyse neden tüm bunları bana anlatıyorsun?
Yani an'ın ne olduğu anlayışıyla Expression<>
geri dönebiliriz Html.TextBoxFor
. Bir metin kutusu oluşturduğunda, ona verdiğiniz özellik hakkında birkaç şey üretmesi gerekir. Şeyler gibi attributes
doğrulama için mülkiyet ve özellikle bu durumda ne anlamaya ihtiyacı isim<input>
etiketi.
Bunu ifade ağacını "yürüyerek" ve bir isim oluşturarak yapar. Yani, gibi bir ifade için model=>model.SomeProperty
, istediğiniz özellikleri toplayan ve inşa ettiğiniz ifadeyi yürütür <input name='SomeProperty'>
.
Daha karmaşık Örneğin, gibi model=>model.Foo.Bar.Baz.FooBar
, bu oluşturabilir<input name="Foo.Bar.Baz.FooBar" value="[whatever FooBar is]" />
Mantıklı olmak? O sadece iş değil Func<>
, ama nasıl öyle çalışmalarını burada önemlidir.
(LINQ to SQL gibi diğer çerçevelerin bir ifade ağacında yürüyüp farklı bir dilbilgisi oluşturarak benzer şeyler yaptığını unutmayın; bu durumda bir SQL sorgusu)
Model Bağlayıcı nasıl çalışır?
Yani bunu anladıktan sonra, kısaca model bağlayıcı hakkında konuşmalıyız. Form gönderildiğinde, sadece bir daire gibidir
Dictionary<string, string>
, iç içe geçmiş görünüm modelimizin sahip olabileceği hiyerarşik yapıyı kaybetmiş oluruz. Bu anahtar-değer çifti kombinasyonunu alıp bazı özelliklerle bir nesneyi yeniden sulandırmaya çalışmak model bağlayıcının görevidir. Bunu nasıl yapıyor? Gönderilen girişin "anahtarını" veya adını kullanarak bunu tahmin ettiniz.
Yani form gönderisi şöyle görünüyorsa
Foo.Bar.Baz.FooBar = Hello
Ve denilen bir modele gönderi yayınlıyorsunuz SomeViewModel
, sonra yardımcının ilk başta yaptığının tersini yapıyor. "Foo" adlı bir özelliği arar. Sonra "Foo" dan "Bar" adlı bir mülk arar, sonra "Baz" ı arar ... vb ...
Son olarak, değeri "FooBar" türüne ayrıştırmaya ve onu "FooBar" a atamaya çalışır.
PHEW !!!
Ve voila, senin modelin var. Model Binder'in henüz oluşturduğu örnek, istenen Eyleme teslim edilir.
Yani çözümünüz işe yaramıyor çünkü Html.[Type]For()
yardımcıların bir ifadeye ihtiyacı var. Ve sen onlara bir değer veriyorsun. Bu değer için bağlamın ne olduğu hakkında hiçbir fikri yoktur ve onunla ne yapacağını bilemez.
Şimdi bazı insanlar oluşturmak için parçaların kullanılmasını önerdi. Şimdi bu teoride işe yarayacak, ancak muhtemelen beklediğiniz gibi değil. Bir parçayı işlediğinizde, türünü değiştirirsiniz TModel
çünkü farklı bir görünüm bağlamındasınız. Bu, mülkünüzü daha kısa bir ifadeyle tanımlayabileceğiniz anlamına gelir. Ayrıca, yardımcı, ifadeniz için isim oluşturduğunda, sığ olacağı anlamına gelir. Yalnızca verildiği ifadeye göre oluşturulur (tüm bağlama göre değil).
Diyelim ki az önce "Baz" (önceki örneğimizden) oluşturulmuş bir kısmınız var. Bu parçanın içinde şöyle diyebilirsiniz:
@Html.TextBoxFor(model=>model.FooBar)
Ziyade
@Html.TextBoxFor(model=>model.Foo.Bar.Baz.FooBar)
Bu, bunun gibi bir giriş etiketi oluşturacağı anlamına gelir:
<input name="FooBar" />
Bu formu, derinlemesine iç içe geçmiş bir ViewModel bekleyen bir eyleme gönderiyorsanız, o zaman FooBar
iptal edilen bir özelliği hidratlamaya çalışacaktır TModel
. Hangisi en iyi ihtimalle orada değildir ve en kötüsü tamamen başka bir şeydir. Baz
Kök model yerine a'yı kabul eden belirli bir eyleme gönderi gönderiyorsanız , bu harika olur! Aslında, bölümler görünüm bağlamınızı değiştirmenin iyi bir yoludur, örneğin, tümü farklı eylemlere gönderen birden çok forma sahip bir sayfanız varsa, her biri için bir bölüm oluşturmak harika bir fikir olacaktır.
Şimdi tüm bunları aldığınızda, Expression<>
programatik olarak genişleterek ve onlarla başka düzgün şeyler yaparak gerçekten ilginç şeyler yapmaya başlayabilirsiniz . Ben bunlara girmeyeceğim. Ancak umarım bu size perde arkasında neler olup bittiğini ve olayların neden olduğu gibi davrandığını daha iyi anlamanızı sağlar.
@
şeyden önce sahip olmanız gerekmezforeach
mi? AyrıcaHtml.EditorFor
(Html.EditorFor(m => m.Note)
örneğin) ve diğer yöntemlerde de lambdas olması gerekmez mi ? Yanılıyor olabilirim, ancak lütfen gerçek kodunuzu yapıştırır mısınız? MVC'de oldukça yeniyim, ancak bunu kısmi görünümlerle veya editörlerle (eğer isim buysa?) Oldukça kolay bir şekilde çözebilirsiniz.