我有一个问题,当表单发布到控制器进行更新时,我的剃刀视图没有向控制器提供更新的模型对象。
该视图旨在允许用户更改现有记录并将其保存回数据库。使用'@foreach'在表单上显示多个可更新的记录。
控制器正在向视图提供正确的数据(GET),但无论我尝试什么,每次回发或“保存”数据时,返回控制器的对象都是原始对象。当用户返回控制器以保存更新时,用户在视图中所做的任何更改都不会反映在对象中。
P.S。不要被我的视图中的术语“控制器”混淆 - 这是正在更新的实体名称,而不是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);
}
}
以下是查看代码:
@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")
}
呈现给浏览器的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%2015%3A58%3A15&RecordDeleted=True" method="post">
//lots of stuff here ....
</form>
获得POST的URL是预期的;包含来自GET的原始元素:
http://localhost:59259/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%3A10%3A36&RecordDeleted=True
以下是表单中呈现的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>
我在每个人的帮助下取得了一些进展。我是MS MVC实体框架的新手并且是自学成才的,所以我担心我缺乏一些批判性的知识。我已从HTML BeginForm帮助程序中删除了“controller”对象参数,并确保该表单包含所有可用字段。我已将绑定前缀“控制器”添加到控制器上的绑定(非常混乱,我知道)。我现在将空数据返回给控制器,所以我仍然必须做错事。
这是控制器代码:
[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);
}
}
这是Razor View代码:
@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>
}
最后,这是发送到控制器的POST数据:
__RequestVerificationToken:NO1PIEHxKyzlNldFuOOt-
EZhiU5VDe1Ax8CI9xzEzvViSgBqECKzmLrDRIdJeNOzMMJDaA-GTTBt5OeueTvnCKK6hK7MK_51EtANrVjwb5m4zVQY6tlHbnJcw3mdO9m5YhZi2eskyq4NhoHJHooL0g2
controller.Device_ID:1
controller.DownloadFlag:False
controller.LastDownload:
controller.LastUpload:
controller.RecordCreated:5/4/2014 12:00:00 AM
controller.RecordEdited:7/14/2014 4:11:21 PM
controller.RecordDeleted:True
controller.DeviceCode:MX24c
controller.DeviceName:#1-w
controller.TimeZone:-8
controller.ExtBoards:0
controller.Sequential:true
controller.Sequential:false
controller.StationDelay:5
controller.MasterStation:false
controller.MastOnOffset:0
controller.MastOffOffset:0
controller.LocationZip:97124
controller.LocationCity:
controller.LocationCountry:
答案 0 :(得分:1)
您需要在视图中删除routeValues: controller
,然后将[Bind(Prefix = "controller")]
添加到您的操作方法参数中。
您的routeValues: controller
正在掩盖您的问题,而不是解决问题。它将此长查询字符串添加到表单操作,然后添加到发布的URL。发布表单中的数据来自POST请求,而不是来自url,尽管默认模型绑定器可以处理这两者。所以在你的情况下发生的事情是它需要你捏造的这些未改变的数据但它不能绑定回发数据,因为它们与你的视图中的“controller
”变量相关联,并且action方法参数具有另一个名字:tblDevice
。您需要将前缀参数添加到Bind属性以解决此问题。
此外,在这种情况下,我强烈建议查看回发数据,因为它们会立即提供可能出错的线索(在这种情况下是前缀)。大卫在第一篇评论中向你提出这个建议,但你可能忽略了这个建议。
答案 1 :(得分:0)
好的,所以我终于在@zespri和@Shoe的帮助下找到了问题的解决方案(2部分)。第一部分是在视图中删除routeValues: controller
并将[Bind(Prefix = "controller")]
添加到我的POST操作方法参数中。当我继续获得空数据返回时,我发现了第二个关键问题。名为"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);
}
}
Razor View代码在这里:
@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")
}