C# Razor视图未将更新的模型数据发布到控制器-数据绑定失败
我有一个问题,当表单发布到控制器进行更新时,razor视图没有向控制器提供更新的模型对象 该视图旨在允许用户对现有记录进行更改并将其保存回数据库。使用“@foreach”在表单上显示多个可更新记录 控制器正在向视图(GET)提供正确的数据,但无论我如何尝试,每次回发或“保存”数据时,返回给控制器的对象都是原始对象。当对象返回控制器保存更新时,用户在视图中所做的任何更改都不会反映在对象中 注意,在我看来,不要被术语“控制器”弄糊涂了——这是正在更新的实体名称,而不是MVC控制器的名称 以下是控制器代码:C# Razor视图未将更新的模型数据发布到控制器-数据绑定失败,c#,asp.net-mvc,razor,C#,Asp.net Mvc,Razor,我有一个问题,当表单发布到控制器进行更新时,razor视图没有向控制器提供更新的模型对象 该视图旨在允许用户对现有记录进行更改并将其保存回数据库。使用“@foreach”在表单上显示多个可更新记录 控制器正在向视图(GET)提供正确的数据,但无论我如何尝试,每次回发或“保存”数据时,返回给控制器的对象都是原始对象。当对象返回控制器保存更新时,用户在视图中所做的任何更改都不会反映在对象中 注意,在我看来,不要被术语“控制器”弄糊涂了——这是正在更新的实体名称,而不是MVC控制器的名称 以下是控制器
public partial class DeviceStationController : Controller
{
// GET:
public ActionResult MyDevicesSetup()
{
var tblUserDevice = db.TblUserDevices.Include(x => x.TblDevice).Include(x => x.TblDevicePrograms).Include(x => x.TblDeviceSensors).Include(x => x.TblDeviceStations).Include(x => x.TblUser).Where(x => x.TblUser.AspNetUser.UserName == User.Identity.Name);
var UserDevice_IDs = tblUserDevice.Select(p => p.Device_ID).Distinct();
var tblDevice = db.TblDevices.Include(x => x.TblUserDevices).Where(x => UserDevice_IDs.Contains(x.Device_ID));
return View(tblDevice.ToList());
}
// POST:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyDevicesSetup([Bind(Include = "Device_ID,DeviceCode,DeviceName,TimeZone,ExtBoards,Sequential,StationDelay,MasterStation,MastOnOffset,MastOffOffset,LocationZip,LocationCity,LocationCountry,DownloadFlag,LastDownload,LastUpload,RecordCreated,RecordEdited,RecordDeleted")] TblDevice tblDevice)
{
if (ModelState.IsValid)
{
tblDevice.RecordEdited = DateTime.Now;
db.Entry(tblDevice).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("MyDevicesSetup");
}
return View(tblDevice);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
//public ActionResult MyDevicesSetup([Bind(Prefix = "controller")] TblDevice tblDevice)
public ActionResult MyDevicesSetup([Bind(Prefix = "controller", Include = "Device_ID,DeviceCode,DeviceName,TimeZone,ExtBoards,Sequential,StationDelay,MasterStation,MastOnOffset,MastOffOffset,LocationZip,LocationCity,LocationCountry,DownloadFlag,LastDownload,LastUpload,RecordCreated,RecordEdited,RecordDeleted")] TblDevice tblDevice)
{
if (ModelState.IsValid)
{
tblDevice.RecordEdited = DateTime.Now;
db.Entry(tblDevice).State = EntityState.Modified;
db.SaveChanges();
//return RedirectToAction("MyDevicesSetup");
}
return View(tblDevice);
}
}
以下是查看代码:
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var controller in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@controller.DeviceName
</h4>
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation", routeValues: controller))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => controller.Device_ID)
<div class="form-group">
@Html.LabelFor(model => controller.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => controller.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => controller.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
@*More fields here..... removed for brevity...*@
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var controller in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@controller.DeviceName
</h4>
@*@using (Html.BeginForm("MyDevicesSetup", "DeviceStation", routeValues: controller))*@
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => controller.Device_ID)
@Html.HiddenFor(model => controller.DownloadFlag)
@Html.HiddenFor(model => controller.LastDownload)
@Html.HiddenFor(model => controller.LastUpload)
@Html.HiddenFor(model => controller.RecordCreated)
@Html.HiddenFor(model => controller.RecordEdited)
@Html.HiddenFor(model => controller.RecordDeleted)
<div class="form-group">
@Html.LabelFor(model => controller.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => controller.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => controller.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
@*Remaining form fields follow - left out for brevity...*@
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
下面是表单中呈现的更多HTML。物业名称似乎保留得很好,但这超出了我理解“幕后”情况的能力。我感谢所有有益的意见和建议:
<form action="/DeviceStation/MyDevicesSetup?TblUserDevices=System.Collections.Generic.HashSet%601%5BAquaTame.Models.TblUserDevice%5D&Device_ID=1&DeviceCode=MX24B&DeviceName=%231-w&TimeZone=-8&ExtBoards=0&Sequential=True&StationDelay=5&MasterStation=False&MastOnOffset=0&MastOffOffset=0&LocationZip=97124&DownloadFlag=False&RecordCreated=05%2F04%2F2014%2000%3A00%3A00&RecordEdited=07%2F14%2F2014%2016%3A11%3A21&RecordDeleted=True" method="post">
<input name="__RequestVerificationToken" type="hidden" value="7-wHfkHpP50iZ4pekCWhIe0ahkvvE7KapaJjFkEhBfjZwtu8-bBfJvG1Pg-9ILn0FsXnrj8Jq1TJQKrq5DxQkaLxd7AVcxsQjqJegrwJL4VDIeR5H68QEPmqOQOu9AIIfdYzqON-iUDv4dFGg5IkXg2">
<div class="form-horizontal">
<input data-val="true" data-val-number="The field Device_ID must be a number." data-val-required="The Device_ID field is required." id="controller_Device_ID" name="controller.Device_ID" type="hidden" value="1">
<div class="form-group">
<label class="control-label col-md-2" for="controller_DeviceCode">DeviceCode</label>
<div class="col-md-10">
<div class="ui-input-text ui-body-inherit ui-corner-all ui-shadow-inset">
<input class="form-control text-box single-line" id="controller_DeviceCode" name="controller.DeviceCode" type="text" value="MX24B"></div>
<span class="field-validation-valid text-danger" data-valmsg-for="controller.DeviceCode" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="controller_DeviceName">DeviceName</label>
<div class="col-md-10">
<div class="ui-input-text ui-body-inherit ui-corner-all ui-shadow-inset">
<input class="form-control text-box single-line" data-val="true" data-val-length="The field DeviceName must be a string with a maximum length of 8." data-val-length-max="8" id="controller_DeviceName" name="controller.DeviceName" type="text" value="#1-w"></div>
<span class="field-validation-valid text-danger" data-valmsg-for="controller.DeviceName" data-valmsg-replace="true"></span>
</div>
</div>
@*More form fields in here, removed for brevity........*@
<div class="ui-btn ui-input-btn ui-corner-all ui-shadow">Save<input type="submit" value="Save" class="btn btn-default"></div>
</div>
</form>
这是Razor视图代码:
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var controller in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@controller.DeviceName
</h4>
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation", routeValues: controller))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => controller.Device_ID)
<div class="form-group">
@Html.LabelFor(model => controller.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => controller.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => controller.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
@*More fields here..... removed for brevity...*@
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var controller in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@controller.DeviceName
</h4>
@*@using (Html.BeginForm("MyDevicesSetup", "DeviceStation", routeValues: controller))*@
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => controller.Device_ID)
@Html.HiddenFor(model => controller.DownloadFlag)
@Html.HiddenFor(model => controller.LastDownload)
@Html.HiddenFor(model => controller.LastUpload)
@Html.HiddenFor(model => controller.RecordCreated)
@Html.HiddenFor(model => controller.RecordEdited)
@Html.HiddenFor(model => controller.RecordDeleted)
<div class="form-group">
@Html.LabelFor(model => controller.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => controller.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => controller.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
@*Remaining form fields follow - left out for brevity...*@
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
您需要去掉视图中的
routeValues:controller
,而是将[Bind(Prefix=“controller”)]
添加到操作方法参数中
您的routeValues:controller
正在掩盖您的问题,而不是解决它。它将这个长查询字符串添加到表单操作中,然后添加到发布的URL中。来自已发布表单的数据来自POST请求,而不是url,尽管默认的模型绑定器可以同时处理这两个数据。因此,在您的案例中发生的情况是,它接受您伪造的这些未更改的数据,但无法绑定回发数据,因为它们与视图中的“controller
”变量关联,并且action-method参数具有不同的名称:tblDevice
。您需要将prefix参数添加到Bind属性以解决此问题
此外,在这种情况下,我强烈建议查看回发数据,因为它们会立即提供可能出错的线索(在本例中是前缀)。David在第一条评论中向您提出了这一建议,但您可能忽略了这一建议。好的,因此我最终在@zespri和@Shoe的帮助下找到了问题的解决方案(两部分)。第一部分是去掉视图中的
routeValues:controller
,并将[Bind(Prefix=“controller”)]
添加到我的POST-action方法参数中。当我继续返回空数据时,我发现了第二个关键问题。名为“controller”
的变量肯定引起了某种冲突(它是C#中的保留字吗?),因为我一把它改为“cntrlr”,一切都正常了。我仔细检查并再现了这个问题,因此我非常确定使用单词“controller”
作为变量名会导致问题。谢谢大家的帮助
工作控制器代码如下所示:
public partial class DeviceStationController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyDevicesSetup([Bind(Prefix = "ctrlr", Include = "Device_ID,DeviceCode,DeviceName,TimeZone,ExtBoards,Sequential,StationDelay,MasterStation,MastOnOffset,MastOffOffset,LocationZip,LocationCity,LocationCountry,DownloadFlag,LastDownload,LastUpload,RecordCreated,RecordEdited,RecordDeleted")] TblDevice tblDevice)
{
if (ModelState.IsValid)
{
tblDevice.RecordEdited = DateTime.Now;
db.Entry(tblDevice).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("MyDevicesSetup");
}
return View(tblDevice);
}
}
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var ctrlr in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@ctrlr.DeviceName
</h4>
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => ctrlr.Device_ID)
@Html.HiddenFor(model => ctrlr.DownloadFlag)
@Html.HiddenFor(model => ctrlr.LastDownload)
@Html.HiddenFor(model => ctrlr.LastUpload)
@Html.HiddenFor(model => ctrlr.RecordCreated)
@Html.HiddenFor(model => ctrlr.RecordEdited)
@Html.HiddenFor(model => ctrlr.RecordDeleted)
<div class="form-group">
@Html.LabelFor(model => ctrlr.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => ctrlr.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => ctrlr.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => ctrlr.DeviceName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => ctrlr.DeviceName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => ctrlr.DeviceName, "", new { @class = "text-danger" })
</div>
</div>
@* More form fields in here....*@
</div>
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Razor视图代码如下:
public partial class DeviceStationController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MyDevicesSetup([Bind(Prefix = "ctrlr", Include = "Device_ID,DeviceCode,DeviceName,TimeZone,ExtBoards,Sequential,StationDelay,MasterStation,MastOnOffset,MastOffOffset,LocationZip,LocationCity,LocationCountry,DownloadFlag,LastDownload,LastUpload,RecordCreated,RecordEdited,RecordDeleted")] TblDevice tblDevice)
{
if (ModelState.IsValid)
{
tblDevice.RecordEdited = DateTime.Now;
db.Entry(tblDevice).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("MyDevicesSetup");
}
return View(tblDevice);
}
}
@model IEnumerable<AquaTame.Models.TblDevice>
@{
ViewBag.Title = "Controller Setup";
}
@foreach (var ctrlr in Model)
{
<div data-role="collapsible" data-collapsed-icon="carat-d" data-expanded-icon="carat-u" data-iconpos="right">
<h4>
@ctrlr.DeviceName
</h4>
@using (Html.BeginForm("MyDevicesSetup", "DeviceStation"))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => ctrlr.Device_ID)
@Html.HiddenFor(model => ctrlr.DownloadFlag)
@Html.HiddenFor(model => ctrlr.LastDownload)
@Html.HiddenFor(model => ctrlr.LastUpload)
@Html.HiddenFor(model => ctrlr.RecordCreated)
@Html.HiddenFor(model => ctrlr.RecordEdited)
@Html.HiddenFor(model => ctrlr.RecordDeleted)
<div class="form-group">
@Html.LabelFor(model => ctrlr.DeviceCode, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => ctrlr.DeviceCode, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => ctrlr.DeviceCode, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => ctrlr.DeviceName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => ctrlr.DeviceName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => ctrlr.DeviceName, "", new { @class = "text-danger" })
</div>
</div>
@* More form fields in here....*@
</div>
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
@model IEnumerable
@{
ViewBag.Title=“控制器设置”;
}
@foreach(模型中的var ctrlr)
{
@ctrlr.DeviceName
@使用(Html.BeginForm(“MyDeviceSetup”、“DeviceStation”))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true,“,new{@class=“text danger”})
@Html.HiddenFor(model=>ctrlr.Device\u ID)
@Html.HiddenFor(model=>ctrlr.DownloadFlag)
@Html.HiddenFor(model=>ctrlr.LastDownload)
@Html.HiddenFor(model=>ctrlr.LastUpload)
@Html.HiddenFor(model=>ctrlr.RecordCreated)
@Html.HiddenFor(model=>ctrlr.RecordEdited)
@Html.HiddenFor(model=>ctrlr.RecordDeleted)
@LabelFor(model=>ctrlr.DeviceCode,htmlAttributes:new{@class=“controllabel col-md-2”})
@EditorFor(model=>ctrlr.DeviceCode,new{htmlAttributes=new{@class=“form control”})
@Html.ValidationMessageFor(model=>ctrlr.DeviceCode,“,new{@class=“text danger”})
@LabelFor(model=>ctrlr.DeviceName,htmlAttributes:new{@class=“controllabel col-md-2”})
@EditorFor(model=>ctrlr.DeviceName,new{htmlAttributes=new{@class=“form control”})
@Html.ValidationMessageFor(model=>ctrlr.DeviceName,”,新的{@class=“text danger”})
@*这里有更多表单字段*@
}
}
@节脚本{
@Scripts.Render(“~/bundles/jqueryval”)
}
当您选择一个表单并提交它时,该表单的实际HTML标记是什么?POST请求中捕获的值是什么?(您可以使用浏览器调试工具(如Firebug或Chrome工具)检查它们。)用for loopOn替换每个帖子,数据库中的数据是否正在更新?routeValues:controller的用途是什么?我怀疑模型绑定器正在使用它绑定到。@Dbloch-数据库中的数据正在更新,但它是旧值,而不是用户所做的编辑。。控制器正在发布的模型对象不包含用户在视图中所做的编辑。非常感谢您的帮助。我是全新的,在微软MVC实体框架自学成才,所以请原谅我缺乏一定的知识。我现在了解了绑定前缀和使用内置Chrome调试器查看POST数据。这确实非常有用。我仍然需要将空值返回到