MVC的Razor视图嵌套的foreach模型 [英] MVC Razor view nested foreach's model

查看:199
本文介绍了MVC的Razor视图嵌套的foreach模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

试想一个常见的​​场景,这是什么我来跨越简单的版本。其实,我对矿井一对夫妇的进一步嵌套层....

Imagine a common scenario, this is a simpler version of what I'm coming across. I actually have a couple of layers of further nesting on mine....

不过,这是该方案

主题包含列表
类别包含列表
产品包含列表

Theme contains List Category contains List Product contains List

我的控制器提供了一个完全填充的主题,所有的类别为主题,类别内的产品和他们的订单。

My Controller provides a fully populated Theme, with all the Categories for that theme, the Products within this categories and the their orders.

该命令集合有需要可编辑的属性称为数量(以及很多其他)。

The orders collection has a property called Quantity (amongst many others) that needs to be editable.

@model ViewModels.MyViewModels.Theme

@Html.LabelFor(Model.Theme.name)
@foreach (var category in Model.Theme)
{
   @Html.LabelFor(category.name)
   @foreach(var product in theme.Products)
   {
      @Html.LabelFor(product.name)
      @foreach(var order in product.Orders)
      {
          @Html.TextBoxFor(order.Quantity)
          @Html.TextAreaFor(order.Note)
          @Html.EditorFor(order.DateRequestedDeliveryFor)
      }
   }
}

如果我使用的lambda,而不是再然后,我只能似乎得到顶端Model对象,主题的参考而不是那些在foreach循环中。

If I use lambda instead then then I only seem to get a reference to the top Model object, "Theme" not those within the foreach loop.

是什么,我试图做甚至有可能或有我高估或误解什么是可能的?

Is what I'm trying to do there even possible or have I overestimated or misunderstood what is possible?

通过上面我得到一个错误的TextboxFor,EditorFor等

With the above I get an error on the TextboxFor, EditorFor, etc

CS0411:该类型参数的方法
  System.Web.Mvc.Html.InputExtensions.TextBoxFor(System.Web.Mvc.HtmlHelper,
  System.Linq.Ex pressions.Ex pression>)
  不能从使用推断。尝试指定类型参数
  明确。

CS0411: The type arguments for method 'System.Web.Mvc.Html.InputExtensions.TextBoxFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

感谢。

推荐答案

简单的回答很到位的为()循环>的foreach()循环。是这样的:

The quick answer is to use a for() loop in place of your foreach() loops. Something like:

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

不过这掩盖了的为什么的这个解决问题。

有,你至少有一个粗略的了解三件事,才能解决这个问题。我有
不得不承认,我货物culted
很长一段时间,当我开始与框架的工作。我花了相当长的一段
真正获得了什么事。

There are three things that you have at least a cursory understanding before you can resolve this issue. I have to admit that I cargo-culted this for a long time when I started working with the framework. And it took me quite a while to really get what was going on.

这三个东西是:


  • 如何在 LabelFor ...对于的助手工作的MVC?

  • 什么是防爆pression树?

  • 如何模型绑定工作?

  • How do the LabelFor and other ...For helpers work in MVC?
  • What is an Expression Tree?
  • How does the Model Binder work?

所有这三个概念联系在一起,得到答案。

All three of these concepts link together to get an answer.

所以,你已经使用了的HtmlHelper&LT; T&gt;作为 LabelFor 扩展和 TextBoxFor 和其他人,
你可能已经注意到,当你调用它们,你通过他们的Lambda和它的神奇的产生
一些HTML。但如何?

So, you've used the HtmlHelper<T> extensions for LabelFor and TextBoxFor and others, and you probably noticed that when you invoke them, you pass them a lambda and it magically generates some html. But how?

所以要注意到的第一件事是这些佣工签名。让我们看一下最简单的过载
TextBoxFor

So the first thing to notice is the signature for these helpers. Lets look at the simplest overload for TextBoxFor

public static MvcHtmlString TextBoxFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression
) 

首先,这是一个扩展方法强类型的HtmlHelper ,类型&LT;的TModel&GT; 。所以,简单地
国家在幕后发生了什么,当剃刀呈现这种观点它生成的类。
这里面类是实例的HtmlHelper&LT;的TModel&GT; (因为这就是为什么你可以使用属性 HTML @Html ... ),
其中,的TModel 是在 @model 语句定义的类型。所以你的情况,当你正在寻找这个观点的TModel
永远是类型 ViewModels.MyViewModels.Theme

First, this is an extension method for a strongly typed HtmlHelper, of type <TModel>. So, to simply state what happens behind the scenes, when razor renders this view it generates a class. Inside of this class is an instance of HtmlHelper<TModel> (as the property Html, which is why you can use @Html...), where TModel is the type defined in your @model statement. So in your case, when you are looking at this view TModel will always be of the type ViewModels.MyViewModels.Theme.

现在,下一个参数是有点棘手。因此,在调用让我们来看看

Now, the next argument is a bit tricky. So lets look at an invocation

@Html.TextBoxFor(model=>model.SomeProperty);

看起来我们有一点点的lambda,如果一个人猜的签名,人们可能会认为类型
这种说法,简直是一个 Func键&LT;的TModel,TProperty&GT; ,其中的TModel 是视图模型和类型 TProperty
推测作为属性的类型。

It looks like we have a little lambda, And if one were to guess the signature, one might think that the type for this argument would simply be a Func<TModel, TProperty>, where TModel is the type of the view model and TProperty is inferred as the type of the property.

但是那并不完全正确,如果你看的实际的类型参数的其防爆pression&LT; Func键&LT;的TModel,TProperty&GT;&GT;

But thats not quite right, if you look at the actual type of the argument its Expression<Func<TModel, TProperty>>.

所以,当你通常产生的lambda,编译器会拉姆达并对其进行编译成MSIL,就像任何其他
功能(这就是为什么你可以使用委托,方法组和lambda表达式或多或少地互换,因为他们只是
code引用。)

So when you normally generate a lambda, the compiler takes the lambda and compiles it down into MSIL, just like any other function (which is why you can use delegates, method groups, and lambdas more or less interchangeably, because they are just code references.)

然而,当编译器看到该类型是防爆pression&LT;&GT; ,它不会立即编译的lambda下降到MSIL,而是生成
防爆pression树!

However, when the compiler sees that the type is an Expression<>, it doesn't immediately compile the lambda down to MSIL, instead it generates an Expression Tree!

那么,到底是什么一个前pression树。好吧,它并不复杂,但它不是在公园里散步无论是。引用MS:

So, what the heck is an expression tree. Well, it's not complicated but its not a walk in the park either. To quote ms:

|实施例pression树木重新present $ C $下在树状数据结构,其中每个节点是一个前pression,例如,一个方法调用或二进制操作如x&下;年。

| Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y.

简单地说,一个前pression树是一种功能的行动的集合再presentation。

Simply put, an expression tree is a representation of a function as a collection of "actions".

的情况下,模型= GT; model.SomeProperty ,前pression树将在这一个节点,上面写着:获取一些房产从模式

In the case of model=>model.SomeProperty, the expression tree would have a node in it that says: "Get 'Some Property' from a 'model'"

这前pression树可以的编译的成可以被调用,但只要它是一个前pression树,它只是一个节点的集合功能。

This expression tree can be compiled into a function that can be invoked, but as long as it's an expression tree, it's just a collection of nodes.

所以 Func键&LT;&GT; 动作&LT;&GT; ,一旦你拥有了他们,他们是pretty多原子。所有你能真正做的是的invoke()其中,又名告诉他们
做自己应该做的工作。

So Func<> or Action<>, once you have them, they are pretty much atomic. All you can really do is Invoke() them, aka tell them to do the work they are supposed to do.

防爆pression&LT; Func键&LT;&GT;&GT; ,另一方面,重新presents的行为集合,它可以追加,操纵, <一href=\"http://msdn.microsoft.com/en-us/library/system.linq.ex$p$pssions.ex$p$pssionvisitor.aspx\">visited,或编译并调用。

Expression<Func<>> on the other hand, represents a collection of actions, which can be appended, manipulated, visited, or compiled and invoked.

因此​​,与这种理解什么是防爆pression&LT;&GT; 是,我们可以回到 Html.TextBoxFor 。当它呈现一个文本框,它需要
产生了一些东​​西关于的,你给它的财产。像属性的东西对物业进行验证,并专门
在这种情况下就需要弄清楚如何的名称的的&LT;输入方式&gt; 标签

So with that understanding of what an Expression<> is, we can go back to Html.TextBoxFor. When it renders a textbox, it needs to generate a few things about the property that you are giving it. Things like attributes on the property for validation, and specifically in this case it needs to figure out what to name the <input> tag.

它通过走恩pression树,建设一个名字这一点。因此,对于一个前pression像模式=&GT; model.SomeProperty ,它走的前pression
收集你所要求的,并建立属性&LT;输入名称='SomeProperty'方式&gt;

It does this by "walking" the expression tree and building a name. So for an expression like model=>model.SomeProperty, it walks the expression gathering the properties that you are asking for and builds <input name='SomeProperty'>.

有关更复杂的例子,如模式=&GT; model.Foo.Bar.Baz.FooBar ,它可能会产生&LT;输入名字=Foo.Bar.Baz.FooBar值=[无论是FooBar的]/&GT;

For a more complicated example, like model=>model.Foo.Bar.Baz.FooBar, it might generate <input name="Foo.Bar.Baz.FooBar" value="[whatever FooBar is]" />

有意义吗?这不仅是工作的 Func键&LT;&GT; 的做法,但如何的它的工作是很重要的位置。

Make sense? It is not just the work that the Func<> does, but how it does its work is important here.

(注意其他框架类似的LINQ to SQL步行一个前pression树,建设不同的语法做类似的事情,那这种情况下,SQL查询)

(Note other frameworks like LINQ to SQL do similar things by walking an expression tree and building a different grammar, that this case a SQL query)

所以,一旦你的是,我们要简单介绍一下模型绑定。当窗体被发布,它只是像一个平
词典&LT;字符串,字符串&GT; ,我们已经失去了分层结构嵌套我们的视图模型可能有。这是
模型绑定的工作借此键值对组合,并尝试补充水分一个对象的一些属性。它是如何做到
这个?你猜对了,通过使用该贴得到了输入的钥匙或名称。

So once you get that, we have to briefly talk about the model binder. When the form gets posted, it's simply like a flat Dictionary<string, string>, we have lost the hierarchical structure our nested view model may have had. It's the model binder's job to take this key-value pair combo and attempt to rehydrate an object with some properties. How does it do this? You guessed it, by using the "key" or name of the input that got posted.

因此​​,如果表单提交的样子

So if the form post looks like

Foo.Bar.Baz.FooBar = Hello

和你发布一个叫做模型 SomeViewModel ,然后它做什么样的帮手首先做了相反的。它看起来
一个名为foo的属性。然后,它查找一个名为酒吧关闭富的属性,那么它寻找巴兹...等等...

And you are posting to a model called SomeViewModel, then it does the reverse of what the helper did in the first place. It looks for a property called "Foo". Then it looks for a property called "Bar" off of "Foo", then it looks for "Baz"... and so on...

最后,它试图将价值解析为FooBar的的类型,并指定为FooBar的。

Finally it tries to parse the value into the type of "FooBar" and assign it to "FooBar".

唷!

和中提琴,你有你的模型。模型绑定刚刚建成的情况下被移交到要求的动作。

And viola, you have your model. The instance the Model Binder just constructed gets handed into requested Action.

所以你的解决方案不起作用,因为 HTML。[类型]对于()助手需要一个前pression。而你只是给他们一个值。它不知道
什么情况下是该值,它不知道该怎么办。

So your solution doesn't work because the Html.[Type]For() helpers need an expression. And you are just giving them a value. It has no idea what the context is for that value, and it doesn't know what to do with it.

现在一些人用谐音来呈现建议。现在,这在理论上可行,但可能不是你所期望的方式。当你渲染的部分,您将更改的TModel 的类型,因为你是在一个不同的视图环境。这意味着,你能描述
用更短的前pression你的财产。当帮手生成你的前pression名字这也意味着,这将是浅薄。它
只会生成基于它给前任pression(不是整个上下文)。

Now some people suggested using partials to render. Now this in theory will work, but probably not the way that you expect. When you render a partial, you are changing the type of TModel, because you are in a different view context. This means that you can describe your property with a shorter expression. It also means when the helper generates the name for your expression, it will be shallow. It will only generate based on the expression it's given (not the entire context).

因此​​,可以说你有一个部分,只是呈现的巴兹(从我们的例子之前)。里面的那个部分,你可以只说:

So lets say you had a partial that just rendered "Baz" (from our example before). Inside that partial you could just say:

@Html.TextBoxFor(model=>model.FooBar)

而不是

@Html.TextBoxFor(model=>model.Foo.Bar.Baz.FooBar)

这意味着,它会产生一个像这样的输入标签:

That means that it will generate an input tag like this:

<input name="FooBar" />

其中,如果你正在张贴这种形式给需要一个大的深度嵌套视图模型的动作,那么它会尝试水合物财产
名为 FooBar的关闭的TModel 的。这充其量是不存在的,在最坏的情况完全是另一回事。如果你发布到一个特定的动作被接受巴兹,而不是根模型,那么这将工作的伟大!事实上,谐音是改变你的看法方面,例如,如果你有多种形式的网页,所有的帖子不同的动作,然后在渲染部分为每个人都成为一个伟大的想法的好办法。

Which, if you are posting this form to an action that is expecting a large deeply nested ViewModel, then it will try to hydrate a property called FooBar off of TModel. Which at best isn't there, and at worst is something else entirely. If you were posting to a specific action that was accepting a Baz, rather than the root model, then this would work great! In fact, partials are a good way to change your view context, for example if you had a page with multiple forms that all post to different actions, then rendering a partial for each one would be a great idea.

现在,一旦你得到这一切,你就可以开始使用做到真正有趣的事情防爆pression&LT;&GT; ,通过编程扩展它们和做
其他巧妙的事情与他们。我不会得到任何这一点。但是,希望这将
给你一个更好的了解是怎么回事幕后,为什么事情正在采取行动,他们是这样的。

Now once you get all of this, you can start to do really interesting things with Expression<>, by programatically extending them and doing other neat things with them. I won't get into any of that. But, hopefully, this will give you a better understanding of what is going on behind the scenes and why things are acting the way that they are.

这篇关于MVC的Razor视图嵌套的foreach模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆