C# Razor视图未将更新的模型数据发布到控制器-数据绑定失败

C# Razor视图未将更新的模型数据发布到控制器-数据绑定失败,c#,asp.net-mvc,razor,C#,Asp.net Mvc,Razor,我有一个问题,当表单发布到控制器进行更新时,razor视图没有向控制器提供更新的模型对象 该视图旨在允许用户对现有记录进行更改并将其保存回数据库。使用“@foreach”在表单上显示多个可更新记录 控制器正在向视图(GET)提供正确的数据,但无论我如何尝试,每次回发或“保存”数据时,返回给控制器的对象都是原始对象。当对象返回控制器保存更新时,用户在视图中所做的任何更改都不会反映在对象中 注意,在我看来,不要被术语“控制器”弄糊涂了——这是正在更新的实体名称,而不是MVC控制器的名称 以下是控制器

我有一个问题,当表单发布到控制器进行更新时,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&amp;Device_ID=1&amp;DeviceCode=MX24B&amp;DeviceName=%231-w&amp;TimeZone=-8&amp;ExtBoards=0&amp;Sequential=True&amp;StationDelay=5&amp;MasterStation=False&amp;MastOnOffset=0&amp;MastOffOffset=0&amp;LocationZip=97124&amp;DownloadFlag=False&amp;RecordCreated=05%2F04%2F2014%2000%3A00%3A00&amp;RecordEdited=07%2F14%2F2014%2016%3A11%3A21&amp;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数据。这确实非常有用。我仍然需要将空值返回到