使用动态属性验证ASP.net MVC3模型

时间:2012-02-23 18:35:35

标签: c# asp.net .net asp.net-mvc-3 web-applications

我正在开发一个应用程序,我需要能够在运行时从数据库加载对象属性。客户希望能够向数据库添加属性并将其显示在应用程序中。我通过为我的模型提供包含名称,类型和值的Field对象列表来实现此目的。这适用于显示和编辑项目属性,但我在编辑器视图中遇到验证问题。谢谢你的帮助。

我希望能够在我的编辑操作中执行此操作:

[HttpPost]
public ActionResult Edit(Movie movie) 
{
    if (ModelState.IsValid) 
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

普通视图:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(model => model.ID)

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
    </fieldset>
}

我需要做什么:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(model => model.ID)

        for(i = 1 to n) {
            <div class="editor-label">
                @Html.LabelFor(model => model.Fields[i].Name)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Fields[i].Value)
                @Html.ValidationMessageFor(model => model.Fields[i].Value)
            </div>
        }
        </fieldset>
}

型号:

public class Movie
{
    public Movie()
    {
        this.Fields = new List<Field>();
    }
    public List<Movie> Movies { get; set; }
}

现场分类:

public class Field
{
    public string Name { get; set; }
    public Type Type { get; set; }
    public object Value { get; set; }
} 

1 个答案:

答案 0 :(得分:2)

我相信这样的事情应该有效:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(model => model.ID)
        @{int i = 0;}
        @foreach(var field in model.Fields) {
            var htmlFieldName = string.Format("Fields[{0}]", i);

            <div class="editor-label">
                <label for="@htmlFieldName">@field.Title</label>
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => field, null, htmlFieldName)
                @Html.ValidationMessage(htmlFieldName)
            </div>
        }
        </fieldset>
}

(请注意,我编写了如何制作标签文字,因为使用实际值作为标签对我来说没有意义。)

POST应该以这样的值结束:

ID=123
Fields[0]=Jaws
Fields[1]=VeggieTales
...

...并且应该自动绑定到您的Movie模型,前提是该模型具有一个名为Fields的List<string>。如果你的模型看起来不那样,那至少应该让你走上正确的轨道。

更新

在评论中,您解释说您正在尝试为对象生成编辑器。这里有两个主要难点:

  1. MVC依赖于您为EditorFor提供的lambda表达式中返回的静态类型,以确定它应该生成哪种编辑器。要覆盖此内容,您需要提供一个特定的模板名称,我的原始建议会显示您提供null

    @Html.EditorFor(model => field.Value, field.Type.Name, htmlFieldName + ".Value")
    

    您可能需要调整它以使其为Integer等类型提供正确的模板名称,但这应该为您提供一般的想法。

  2. 回发后,服务器无法知道Field[0]int等。您可以:

    1. 提供隐藏值以指定每种类型,然后使用可以使用此信息的自定义模型绑定器根据组合的类型和值构建每个字段。
    2. 根据Movie的ID在服务器端重新创建Movie对象的结构,然后遍历每个调用的字段:

      TryUpdateModel((dynamic)field, string.Format("Field[{0}]", i));
      
  3. 可能还有其他选择,但这是我今天愿意投入的所有时间。