gpt4 book ai didi

c# - 发布一个集合和 ModelState

转载 作者:行者123 更新时间:2023-11-30 12:13:46 27 4
gpt4 key购买 nike

我的 MVC 应用程序中有一个问题,我不确定如何解决,或者我是否以错误的方式解决了这个问题。

我有一个 Controller / View ,它在带有复选框的网格中显示项目列表,当项目发布到我的 Controller 时,我想根据传入的 ID 从我的数据库中删除行。

View 看起来像这样:

@for(int index = 0; index < Model.Items.Length; index++)
{
<td>
@Html.HiddenFor(m => m[index].Id)
@Html.CheckBoxFor(m => m[index].Delete)
</td>
}

我的 Controller 接受这些值:

[HttpPost]
public ActionResult Delete(DeleteItemsModel model)
{
if( !ModelState.IsValid )
{
// ...
}

foreach( var id in model.Items.Where(i => i.Delete))
repo.Delete(id);
}

这个场景工作正常。项目正确发布,带有 id 和要删除或不删除的标志,并且它们已被正确删除。我遇到的问题是我的页面验证失败。我需要再次从数据库中获取项目并将数据发送回 View :

if( !ModelState.IsValid )
{
var order = repo.GetOrder(id);

// map
return View(Mapper.Map<Order, OrderModel>(order));
}

在用户获得要删除的项目列表和他们单击“提交”之间的时间里,可能已经添加了新项目。现在,当我提取数据并将其发送回 View 时,列表中可能会有新项目。

问题示例:
我在我的页面上执行 HTTP GET,我的网格中有两个项目,Id 分别为 2 和 1。我选择第一行(Id 2,按最近排序),然后单击提交。页面验证失败,我将 View 返回给用户。现在网格中有三行 (3, 2, 1)。 MVC 将在第一个项目上有复选框(现在 id 为 3)。如果用户不检查此数据,则他们可能会删除错误的内容。

关于如何解决这种情况或我应该怎么做的任何想法?有没有人知道如何做

最佳答案

有趣的问题。让我们首先用一个简单的例子来说明问题,因为从其他答案来看我不确定每个人都明白这里的问题是什么。

假设有以下模型:

public class MyViewModel
{
public int Id { get; set; }
public bool Delete { get; set; }
}

以下 Controller :

public class HomeController : Controller
{
public ActionResult Index()
{
// Initially we have 2 items in the database
var model = new[]
{
new MyViewModel { Id = 2 },
new MyViewModel { Id = 1 }
};
return View(model);
}

[HttpDelete]
public ActionResult Index(MyViewModel[] model)
{
// simulate a validation error
ModelState.AddModelError("", "some error occured");

if (!ModelState.IsValid)
{
// We refetch the items from the database except that
// a new item was added in the beginning by some other user
// in between
var newModel = new[]
{
new MyViewModel { Id = 3 },
new MyViewModel { Id = 2 },
new MyViewModel { Id = 1 }
};

return View(newModel);
}

// TODO: here we do the actual delete

return RedirectToAction("Index");
}
}

和一个 View :

@model MyViewModel[]

@Html.ValidationSummary()

@using (Html.BeginForm())
{
@Html.HttpMethodOverride(HttpVerbs.Delete)
for (int i = 0; i < Model.Length; i++)
{
<div>
@Html.HiddenFor(m => m[i].Id)
@Html.CheckBoxFor(m => m[i].Delete)
@Model[i].Id
</div>
}
<button type="submit">Delete</button>
}

接下来会发生什么:

用户导航到 Index 操作,选择要删除的第一个项目并单击“删除”按钮。以下是他提交表单之前的 View :

enter image description here

Delete 操作被调用,当 View 再次呈现时(因为存在一些验证错误),用户将看到以下内容:

enter image description here

看看如何预选错误的项目?

为什么会这样?因为 HTML 帮助程序在绑定(bind)时优先使用 ModelState 值而不是模型值,这是设计使然。

那么如何解决这个问题呢?通过阅读 Phil Haack 的以下博客文章:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

在他的博客文章中,他谈到了非顺序索引并给出了以下示例:

<form method="post" action="/Home/Create">

<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />

<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />

<input type="hidden" name="products.Index" value="caliente" />
<input type="text" name="products[caliente].Name" value="Salsa" />
<input type="text" name="products[caliente].Price" value="1.23" />

<input type="submit" />
</form>

看看我们如何不再对输入按钮的名称使用增量索引?

我们如何将其应用到我们的示例中?

像这样:

@model MyViewModel[]
@Html.ValidationSummary()
@using (Html.BeginForm())
{
@Html.HttpMethodOverride(HttpVerbs.Delete)
for (int i = 0; i < Model.Length; i++)
{
<div>
@Html.Hidden("index", Model[i].Id)
@Html.Hidden("[" + Model[i].Id + "].Id", Model[i].Id)
@Html.CheckBox("[" + Model[i].Id + "].Delete", Model[i].Delete)
@Model[i].Id
</div>
}
<button type="submit">Delete</button>
}

现在问题解决了。或者是吗?您看到 View 现在呈现的可怕困惑了吗?我们已经解决了一个问题,但我们在 View 中引入了一些绝对令人厌恶的东西。我不了解你,但当我看到这个时,我想吐。

那么可以做什么呢?我们应该阅读 Steven Sanderson 的博文:http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/他在其中展示了一个非常有趣的自定义 Html.BeginCollectionItem 帮助程序,其用法如下:

<div class="editorRow">
<% using(Html.BeginCollectionItem("gifts")) { %>
Item: <%= Html.TextBoxFor(x => x.Name) %>
Value: $<%= Html.TextBoxFor(x => x.Price, new { size = 4 }) %>
<% } %>
</div>

注意表单元素是如何包装在这个助手中的吗?

这个助手是做什么的?它取代了由 Guids 的强类型助手生成的顺序索引,并使用一个额外的隐藏字段在每次迭代时设置该索引。


也就是说,只有当您需要在删除操作中从数据库中获取新数据时,问题才会出现。如果您依靠模型绑定(bind)器来补水,则根本不会有任何问题(除了如果出现模型错误,您将使用旧数据显示 View -> 这可能根本不是问题):

[HttpDelete]
public ActionResult Index(MyViewModel[] model)
{
// simulate a validation error
ModelState.AddModelError("", "some error occured");

if (!ModelState.IsValid)
{

return View(model);
}

// TODO: here we do the actual delete

return RedirectToAction("Index");
}

关于c# - 发布一个集合和 ModelState,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11089596/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com