ASP.NET MVC Razor 中的嵌套绑定
Nested binding in ASP.NET MVC Razor
描述
嵌套对象需要绑定到下拉列表,嵌套对象已经有预选值。可能的值是枚举类型。带有一些其他数据的下拉列表将被发回控制器。
代码 - 类型 & 类:
[Serializable]
public enum DummyEnum
{
DummyZero = 0,
DummyOne = 1
}
public class Dummy
{
public Guid Id { get; set; }
public Dictionary<Guid, DummyEnum> DummyEnum { get; set; }
}
public class DummyViewModel
{
public Dictionary<Guid, List<Dummy>> Dummies { get; set; }
}
public class DummyController
{
private void Init(DummyViewModel model)
{
model.EnumList = Enum.GetValues(typeof(DummyEnum))
.Cast<DummyEnum>()
.Select(e => new SelectListItem
{
Value = (e).ToString(),
Text = e.ToString()
});
}
}
HTML:
<td>
@Html.DropDownListFor(
m => m.Dummies[dummiesKey][dummyIndex]
.Enum[Id],
new SelectList(Model.EnumList, "Value", "Text", e.ToString()))
</td>
<select
data-val="true"
data-val-required="The Enum field is required."
id="Dummies_guid__0__Enum_guid_"
name="Dummies[guid][0].Enum[guid]"
style="display: none;"
>
<option value="DummyOne">DummyOne</option>
<option selected="selected" value="DummyZero ">DummyZero</option>
</select>
问题
问题是模型似乎无法将有效负载映射回对象或缺少对绑定对象的引用。一切都正确填写了 guid、索引和枚举值。
有效负载:
Dummies[guid][0].Enum[guid]: DummyZero
Dummies[guid][0].Enum[guid]: DummyZero
尝试次数
我尝试了以下想法,但它们对我来说并不成功。
- model not binding to dictionary
- binding dictionary
我错过了什么?
前端响应将采用您将其设置在那里的形式。然后 ASP 中间件会将所有这些字符串解析回后端的一个对象。
这里的关键时刻是:
- 控制器动作参数类型 - 它可以是您的任何类型,但它应该与您的前端相关联;
- 前端的
select
元素 name
属性值 - 它应该包含完整的现有路径。
正如我从您的代码示例中得到的那样。
- 您有
DummyViewModel
个视图模型 class。它有 属性 Dummies
.
- 您有
Dummy
class,嵌套在 DummyViewModel
中作为 Dummies
。二级词典。
- 您有
DummyEnum
个枚举 class,正在使用 DummyEnum
个值。相同的名称,不同的相邻级别。
- SelectList 值正常。它们直接来自枚举。
- 根据结构,要设置第一个枚举值,您需要通过设置 KEY 和 VALUE 来导航其级别。然后再做另一个级别。对我来说,这个结构中的第一个枚举值应该是这样的:
Dummies[dummiesKeyGuid][dummyIndexId].DummyEnum[dummyEnumKeyGuid];
你在每个步骤中有以下类型:
Dummies[dummiesKeyGuid]
是 <List<Dummy>>
;
Dummies[dummiesKeyGuid][dummyIndexId]
是 <Dummy>
;
Dummies[dummiesKeyGuid][dummyIndexId].DummyEnum[dummyEnumKeyGuid]
是 <DummyEnum>
.
所以应该更新 @Html.DropDownListFor(...)
以将路径设置为 name
。
还有:
- 您的操作应将
Dummies
类型作为参数。
ActionResult SomeFromProcessingAction(DummyViewModel Dummies)
- 您应该处理传递的字符串化参数映射到
Dictionary
类型。它可以在 ASP(前端)之外使用,但有问题。请检查此 post 及其主题:。有时,在那里不使用 Dictionary
会更容易。只是其他 class 列表或数组。
根据您的 HTML 示例,我没有得到 m.Dummies
类型在其结构中包含 Enum
字段。 dummiesKey
不能有 "guid"
值。 GUID 是另一种类型,可以很容易地 ToString()
但不是其他类型。
恕我直言。名称中的 Dummy
过多。它混淆并打破了它的理解。
另外,它的嵌套结构非常繁琐。您可以使用较小的用户表单来设置值,并在后端获取较小的对象或事件值,而不是使用庞大的对象参数。
List class 没有映射要求,只需将您的字典反规范化,映射它们会更容易。他们在前端的导航也是如此。如果需要,您可以创建列表 ToDictionary()
。 :)=)
例如,Dummy
可以写成List<T>
:
public class SomeElement
{
public Guid Id { get; set; }
public DummyEnum Enum { get; set; }
}
public class Dummy //kind of aggregation
{
public Guid Id { get; set; }
public List <SomeElement> DummyEnum { get; set; }
}
以此类推DummyViewModel
。或者去掉它,直接使用一些List。
希望这会有所帮助。
我认为您的问题与嵌套属性或输入命名无关,您的实现似乎没问题。
您遇到的问题与 Dictionary<Guid, ...>
默认模型活页夹行为有关。默认的活页夹似乎无法正确处理它。 (即以 Guid
作为键的字典)
我已经重现了你的问题,然后切换到 Dictionary<string, ...>
,这次一切正常。
克服这个问题的唯一方法可能是为 Dictionary<Guid, object>
.
实施您自己的模型绑定器
我试图了解根本问题,它似乎位于 here (Invalid explicit cast from string to Guid) as also described here(稍后发现:-)...)
问题中所述的问题与将 Dictionary
转换为 List> 绑定或使用 JSON
.
的 mvc 有关
所有需要做的就是像 mvc 那样分解对象并提供必要的数据。如 dicitionary binding.
中所述
对象的类型为 Dictionary<Guid, List<Dummy>>
。所以对象实际上变成了List<KeyValuePair<Guid, List<List<KeyValuePair<Guid, enum>>>>>
.
现在分解一下
MVC 需要正在使用的第一个对象的索引。为了获得这个索引,我们需要自己将字典转换为列表。更具体的字典的值或键。
var dummies= Model.Dummies[key];
var dummiesIndex = Model.Dummies.Values.ToList().IndexOf(dummies);
索引需要与 post 一起提供。这可以通过将其作为隐藏字段添加到下拉列表上方以及字典中的键旁边来完成。
@Html.Hidden("dummies.Index", dummiesIndex)
@Html.Hidden("dummies[" + dummiesIndex + "].Key", key)
接下来是对象列表。同样需要为绑定提供索引。
@Html.Hidden("dummies[" + dummiesIndex + "].Value.Index", dummyIndex)
最后一步是另一个字典,这和第一个字典一样
@Html.Hidden("dummies[" + dummiesIndex + "].DummyEnum.Index", dummyEnumIndex)
@Html.Hidden("dummies[" + dummiesIndex + "].DummyEnum.Key", yourKey)
对于您实际想要的值 post 您需要遵循上面的完整路径。
@Html.DefaultCombo("dummies[" + dummiesIndex + "].DummyEnum[" + dummyEnumIndex+ "]", "Value", "Text", Model.EnumList, enum)
现在 MVC 可以重新映射您的对象。
描述
嵌套对象需要绑定到下拉列表,嵌套对象已经有预选值。可能的值是枚举类型。带有一些其他数据的下拉列表将被发回控制器。
代码 - 类型 & 类:
[Serializable]
public enum DummyEnum
{
DummyZero = 0,
DummyOne = 1
}
public class Dummy
{
public Guid Id { get; set; }
public Dictionary<Guid, DummyEnum> DummyEnum { get; set; }
}
public class DummyViewModel
{
public Dictionary<Guid, List<Dummy>> Dummies { get; set; }
}
public class DummyController
{
private void Init(DummyViewModel model)
{
model.EnumList = Enum.GetValues(typeof(DummyEnum))
.Cast<DummyEnum>()
.Select(e => new SelectListItem
{
Value = (e).ToString(),
Text = e.ToString()
});
}
}
HTML:
<td>
@Html.DropDownListFor(
m => m.Dummies[dummiesKey][dummyIndex]
.Enum[Id],
new SelectList(Model.EnumList, "Value", "Text", e.ToString()))
</td>
<select
data-val="true"
data-val-required="The Enum field is required."
id="Dummies_guid__0__Enum_guid_"
name="Dummies[guid][0].Enum[guid]"
style="display: none;"
>
<option value="DummyOne">DummyOne</option>
<option selected="selected" value="DummyZero ">DummyZero</option>
</select>
问题
问题是模型似乎无法将有效负载映射回对象或缺少对绑定对象的引用。一切都正确填写了 guid、索引和枚举值。
有效负载:
Dummies[guid][0].Enum[guid]: DummyZero
Dummies[guid][0].Enum[guid]: DummyZero
尝试次数
我尝试了以下想法,但它们对我来说并不成功。
- model not binding to dictionary
- binding dictionary
我错过了什么?
前端响应将采用您将其设置在那里的形式。然后 ASP 中间件会将所有这些字符串解析回后端的一个对象。
这里的关键时刻是:
- 控制器动作参数类型 - 它可以是您的任何类型,但它应该与您的前端相关联;
- 前端的
select
元素name
属性值 - 它应该包含完整的现有路径。
正如我从您的代码示例中得到的那样。
- 您有
DummyViewModel
个视图模型 class。它有 属性Dummies
. - 您有
Dummy
class,嵌套在DummyViewModel
中作为Dummies
。二级词典。 - 您有
DummyEnum
个枚举 class,正在使用DummyEnum
个值。相同的名称,不同的相邻级别。 - SelectList 值正常。它们直接来自枚举。
- 根据结构,要设置第一个枚举值,您需要通过设置 KEY 和 VALUE 来导航其级别。然后再做另一个级别。对我来说,这个结构中的第一个枚举值应该是这样的:
Dummies[dummiesKeyGuid][dummyIndexId].DummyEnum[dummyEnumKeyGuid];
你在每个步骤中有以下类型:
Dummies[dummiesKeyGuid]
是<List<Dummy>>
;Dummies[dummiesKeyGuid][dummyIndexId]
是<Dummy>
;Dummies[dummiesKeyGuid][dummyIndexId].DummyEnum[dummyEnumKeyGuid]
是<DummyEnum>
.
所以应该更新 @Html.DropDownListFor(...)
以将路径设置为 name
。
还有:
- 您的操作应将
Dummies
类型作为参数。
ActionResult SomeFromProcessingAction(DummyViewModel Dummies)
- 您应该处理传递的字符串化参数映射到
Dictionary
类型。它可以在 ASP(前端)之外使用,但有问题。请检查此 post 及其主题:。有时,在那里不使用Dictionary
会更容易。只是其他 class 列表或数组。
根据您的 HTML 示例,我没有得到 m.Dummies
类型在其结构中包含 Enum
字段。 dummiesKey
不能有 "guid"
值。 GUID 是另一种类型,可以很容易地 ToString()
但不是其他类型。
恕我直言。名称中的 Dummy
过多。它混淆并打破了它的理解。
另外,它的嵌套结构非常繁琐。您可以使用较小的用户表单来设置值,并在后端获取较小的对象或事件值,而不是使用庞大的对象参数。
List class 没有映射要求,只需将您的字典反规范化,映射它们会更容易。他们在前端的导航也是如此。如果需要,您可以创建列表 ToDictionary()
。 :)=)
例如,Dummy
可以写成List<T>
:
public class SomeElement
{
public Guid Id { get; set; }
public DummyEnum Enum { get; set; }
}
public class Dummy //kind of aggregation
{
public Guid Id { get; set; }
public List <SomeElement> DummyEnum { get; set; }
}
以此类推DummyViewModel
。或者去掉它,直接使用一些List。
希望这会有所帮助。
我认为您的问题与嵌套属性或输入命名无关,您的实现似乎没问题。
您遇到的问题与 Dictionary<Guid, ...>
默认模型活页夹行为有关。默认的活页夹似乎无法正确处理它。 (即以 Guid
作为键的字典)
我已经重现了你的问题,然后切换到 Dictionary<string, ...>
,这次一切正常。
克服这个问题的唯一方法可能是为 Dictionary<Guid, object>
.
我试图了解根本问题,它似乎位于 here (Invalid explicit cast from string to Guid) as also described here(稍后发现:-)...)
问题中所述的问题与将 Dictionary
转换为 ListJSON
.
所有需要做的就是像 mvc 那样分解对象并提供必要的数据。如 dicitionary binding.
中所述对象的类型为 Dictionary<Guid, List<Dummy>>
。所以对象实际上变成了List<KeyValuePair<Guid, List<List<KeyValuePair<Guid, enum>>>>>
.
现在分解一下
MVC 需要正在使用的第一个对象的索引。为了获得这个索引,我们需要自己将字典转换为列表。更具体的字典的值或键。
var dummies= Model.Dummies[key];
var dummiesIndex = Model.Dummies.Values.ToList().IndexOf(dummies);
索引需要与 post 一起提供。这可以通过将其作为隐藏字段添加到下拉列表上方以及字典中的键旁边来完成。
@Html.Hidden("dummies.Index", dummiesIndex)
@Html.Hidden("dummies[" + dummiesIndex + "].Key", key)
接下来是对象列表。同样需要为绑定提供索引。
@Html.Hidden("dummies[" + dummiesIndex + "].Value.Index", dummyIndex)
最后一步是另一个字典,这和第一个字典一样
@Html.Hidden("dummies[" + dummiesIndex + "].DummyEnum.Index", dummyEnumIndex)
@Html.Hidden("dummies[" + dummiesIndex + "].DummyEnum.Key", yourKey)
对于您实际想要的值 post 您需要遵循上面的完整路径。
@Html.DefaultCombo("dummies[" + dummiesIndex + "].DummyEnum[" + dummyEnumIndex+ "]", "Value", "Text", Model.EnumList, enum)
现在 MVC 可以重新映射您的对象。