视图显示不同的值,然后在其模型中

View displays different values, then it has in its model

简要概述

我有一个非常奇怪的错误,我完全不知道它来自哪里。我想创建一个网页,您可以在其中修改一个 table 并由此 table 您可以在数据库中生成一个 table。为此,我正在使用 ASP .NET MVC 4 并且我已经能够在数据库中修改和创建 table,但是我有一个小的“+”按钮,我想在其中插入单元格table(模型)。

问题

It already works, but it is inserting it always at the last index AND the name is also equal to the one, which was last before. (same for deleting with "-" button)

这里有两个截图:
点击前: 点击后:

代码

为了实现这一点,我创建了两个模型:

创建表格模型

我用一列初始化模型。 (我还有更多不相关的属性和功能)

public class CreateTableModels
{
    public CreateTableModels()
    {
        Columns = new List<CreateTableColumnModels>() { new CreateTableColumnModels(){ PrimaryKey = true } };
    }

    [Required]
    [UniqueNamesValidation(ErrorMessage="Alle Namen müssen unique sein")]
    [Display(Name = "Columns")]
    public List<CreateTableColumnModels> Columns { get; private set; }

    //more properties...

此模型具有 CreateTableColumnModels 的列表:

public class CreateTableColumnModels
{
    public CreateTableColumnModels()
    {
        Name = "name";
        NotNull = false;
        PrimaryKey = false;
        Length = 1;
    }

    [Required]
    [Display(Name = "Typ")]
    public System.Data.SqlDbType Type { get; set; }

    [Key]
    [Required(ErrorMessage = "Darf nicht leer sein!")]
    [RegularExpression(@"^[A-Za-z0-9]+$", ErrorMessage = "Nur Buchstaben und Zahlen sind erlaubt")]
    [Display(Name = "Name")]
    public string Name { get; set; }

    //more properties...

控制器

我想在控制器中向模型插入一列(如果按下按钮,它会通过 addCell 发送索引)。该列的名称应为 "Added"。如果我添加一个新列,它工作正常。

public class CreateTableController : Controller
{
    public ViewResult Index(CreateTableModels model, int count = 1, int? addCell = null, int? removeCell = null, bool createTable = false)
    {
        if (addCell != null)
        {
            model.Columns.Insert(addCell.Value, new CreateTableColumnModels() { Name = "ADDED", Type = System.Data.SqlDbType.Float });
        }

查看

我几乎把整个视图都放在这里了,因为我完全不知道问题出在哪里。

<h2>CreateTable</h2>

@using (Html.BeginForm("Index", "CreateTable", FormMethod.Get))
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    @Html.ValidationSummary(true)
</div>
<table id="table" class="table">
    <thead>
        <tr>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().Type)</th>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().Length)</th>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().Precision)</th>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().Name)</th>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().NotNull)</th>
            <th>@Html.LabelFor(model => model.Columns.FirstOrDefault().PrimaryKey)</th>
        </tr>
    </thead>
    <tbody>
        @for (int i = 0; i < @Model.Columns.Count; i++)
        {
            <tr>
                <td>
                    <div id="TableSelection">
                        @Html.DropDownListFor(m => Model.Columns[i].Type, new SelectList(Model.Columns[i].Types), new { @ID = "typeSelection", @class = "form-control", @style = "width:150px;" })
                    </div>
                </td>
                <td>
                    <div id="Length">
                        @Html.TextBoxFor(m => Model.Columns[i].Length, new { @ID = "length", @class = "form-control", @type = "number", @style = "width:80px", @disabled = "disabled" })
                        @Html.ValidationMessageFor(m => Model.Columns[i].Length)
                    </div>
                </td>
                <td>
                    <div id="Precision">
                        @Html.TextBoxFor(m => Model.Columns[i].Precision, new { @ID = "precision", @class = "form-control", @type = "number", @style = "width:80px", @disabled = "disabled" })
                        @Html.ValidationMessageFor(m => Model.Columns[i].Precision)
                    </div>
                </td>
                <td>
                    <div id="Name">
                        @Html.TextBoxFor(model => Model.Columns[i].Name, new { @class = "form-control", @style = "width:150px" })
                        @Html.ValidationMessageFor(model => Model.Columns[i].Name)
                    </div>
                </td>
                <td>
                    <div id="NotNull">
                        @Html.CheckBoxFor(model => Model.Columns[i].NotNull, new { @ID = "notNull", @checked = Model.Columns[i].NotNull, @class = "checkbox" })
                    </div>
                </td>
                <td>
                    <div id="PrimaryKey">
                        @Html.CheckBoxFor(x => Model.Columns[i].PrimaryKey, new { @ID = "primaryKey", @checked = Model.Columns[i].PrimaryKey, @class = "checkbox" })
                    </div>
                </td>
                <td>
                    <div>
                        <button type="submit" class="btn btn-default" name="addCell" value="@i">+</button>
                        <button type="submit" class="btn btn-default" name="removeCell" value="@i">-</button>
                    </div>
                </td>
            </tr>
        }
    </tbody>
</table>

<div align="left" class=" form-group" style="display: flex; flex-direction: row; justify-content: flex-start; align-items: center;">
    <div>
        @Html.LabelFor(model => model.TableName, new { @class = "control-label col-md-2" })
    </div>
    <div class="text-center control-form">
        @Html.EditorFor(model => model.TableName, new { @class = "text-center control-form" })
        @Html.ValidationMessageFor(model => model.TableName)
    </div>
    <div>
        <button type="submit" style="margin-left:10px; margin-right:10px;" name="createTable" value="true" class="btn btn-success">Create</button>
    </div>
    <div>
        @if (Model.WorkDone != null && (bool)Model.WorkDone)
        {
            <label style="font-size:medium" class="label label-success">Alle Änderungen wurden gespeichert!</label>
        }
        else if (Model.WorkDone != null)
        {
            <label style="font-size:medium" class="label label-danger">Es ist etwas schief gelaufen!</label>
        }
    </div>
</div>
}

调试

此外,如果我调试它直到视图结束,它具有正确的值。

我唯一能想到的改变是 _Layout.cshtml 文件中的 @RenderBody()。有没有人有类似的东西或可以帮助我?已经感谢阅读到最后:)

问题是当您提交 collection 时,发布的值会添加到 ModelState。如果我们只查看 Name 属性,则会添加以下值

Columns[0].Name // value = "a"
Columns[1].Name // value = "b"
Columns[2].Name // value = "c"

现在插入一个新列(比如在第一项之前)。模型中的值现在是

Columns[0].Name // value = "ADDED"
Columns[1].Name // value = "a"
Columns[2].Name // value = "b"
Columns[3].Name // value = "c"

现在,当您 return 视图时,您(正确地)使用 HtmlHelper 方法生成视图,但是生成表单控件的所有 HtmlHelper 方法都呈现 value 属性通过检查以下(按顺序)

  1. ModelState 中的值(如果有)
  2. ViewDataDictionary 中的值(如果有)
  3. 来自模型的值

此行为的原因在 TextBoxFor displaying initial value, not the value updated from code 中解释。

for 循环的第一次迭代中,您的 @Html.TextBoxFor(m => m.Columns[i].Name)ModelState 中看到 "a" 的值并在文本框中呈现 "a" (即使模型的值为 "ADDED").

在第 2 次迭代中,值再次取自 ModelState,因此呈现 "b"。

在第 3 次迭代中,值再次取自 ModelState,因此呈现 "c"。

并且在第 4 次也是最后一次迭代中,Columns[3].NameModelState(或 ViewData)中找不到值,因此它从模型中读取值,因此 "c" 被渲染。

作为旁注,如果您在 table 单元格中添加 @Model.Columns[i].Name,您会看到根据 属性 值呈现的值,即第 1 行:"ADDED",第 2 行:"a",第 3 行:"b",第 4 行:"c".

要解决此问题,您可以在 return 视图之前添加 ModelState.Clear()。但是,这样做的副作用是它还会清除所有验证错误,因此至少,如果 ModelState 无效

,您应该立即 return 视图
public ViewResult Index(CreateTableModels model, ...)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    ... // code to insert new row
    ModelState.Clear();
    return View(model);
}

但是,最好的方法是遵循 PRG 模式并重定向到生成新数据的方法。