MVC将ModelState键与ViewModel集合进行匹配

时间:2012-10-13 03:09:37

标签: asp.net-mvc-3 validation collections modelstate

当ViewModel是(有)集合时,是否可以将ViewModel属性与匹配的ModelState.Key值匹配?

示例:要编辑viewmodel项的集合,我使用的是extension found here

这会在页面上的字段ID中添加GUID。

示例:

class Pets
{
    string animal;
    string name;
}

对于Pets列表,生成的html源代码如下:

<input name="Pets.index" autocomplete="off" value="3905b306-a9..." type="hidden">
<input value="CAT" id="Pets_3905b306-a9...__animal" name="Pets[3905b306-a9...].animal" type="hidden">
<input value="MR. PEPPERS" id="Pets_3905b306-a9...__name" name="Pets[3905b306-a9...].name" type="hidden">


<input name="Pets.index" autocomplete="off" value="23342306-b4..." type="hidden">
<input value="DOG" id="Pets_23342306-b4...__animal" name="Pets[23342306-b4...].animal" type="hidden">
<input value="BRUTICUS" id="Pets_23342306-b4...__name" name="Pets[23342306-b4...].name" type="hidden">

因此,当它在post上绑定时,ModelState会加载所有表单字段。 在ModelSTate.Keys中,有:

Pets[23342306-b4...].name
Pets[23342306-b4...].animal
Pets[3905b306-a9...].name
Pets[3905b306-a9...].animal

到目前为止一切都很好,但我正在进行一些业务逻辑验证,例如,如果存在同名的新动物,则无法添加新动物。在这种情况下,我希望能够突出显示错误的输入字段。

因此,如果我的create函数失败,它将返回一个错误/键值对,如下所示:

{ error = "Duplicate Name", key="name" }

所以我至少会知道什么财产导致了这个问题。

但由于我的存储库函数不知道视图字段ID,如何将键“name”与相应的ModelState键匹配(在本例中,Pets [23342306-b4 ...]。name或宠物[3905b306-A9 ...]。名)?

2 个答案:

答案 0 :(得分:0)

如果您使用MVC的内置功能来显示具有适当显示/编辑器模板的集合(Html.DisplayFor(m => m.Pets)Html.EditorFor(m => m.Pets)),MVC将呈现如下内容:

Pets[0].name
Pets[0].animal
Pets[1].name
Pets[1].animal

这映射到IEnumerable<Pets>,你知道第一项的索引是0,第二项是等等。

因此,如果第二项有错误,您可以为ModelState"Pets[1].name"设置错误。

答案 1 :(得分:0)

如果您正在使用Html.BeginCollectionItem扩展方法,就像我一样,我可以通过不使用GUID来解决这个问题。我需要动态添加和删除,但我总是查找已知的项目,具有ID的人,我在编辑器中。因此,我只是在下面的代码中分配ID(uniqueId),而不是使用GUID。然后我可以找到钥匙,因为我知道这是人[234232]。当然,如果您要添加新项目而不显示所选项目,则可能对您无效。

        public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName, string uniqueId)
    {
        var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
        string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : uniqueId;

        // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
        html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));

        return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
    }