Context:
I want a user to only update one part [AppStatus] of an entry. However, when I test it, I get the error mentioned in the subject. I've seen similar questions asked, and tried doing some of the steps they outlined (using CASCADE for the FK, for instance). But it didn't work. So it's probably something in the code that I messed up with. I thought maybe, if I adjust the Bind to only include [AppStatus] that would do it, but that didn't work either.
The error throws on two different FKs [StudentID] and [JobPostingID].
Code:
Controller:
public ActionResult Edit([Bind(Include = "ApplicationID,StudentID,JobPostingID,Resume,AppStatus")] Application application)
{
if (ModelState.IsValid)
{
db.Entry(application).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.StudentID = new SelectList(db.Students, "StudentID", "FirstName", application.StudentID);
ViewBag.JobPostingID = new SelectList(db.JobPostings, "JobPostingID", "Position", application.JobPostingID);
return View(application);
}
View:
<div class="form-horizontal">
<h4>Application</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ApplicationID)
<div class="form-group">
#Html.LabelFor(model => model.Student.FirstName, "FirstName", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.Student.FirstName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobPosting.Position, "Position", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.JobPosting.Position)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AppStatus, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.AppStatus, "--Update Application Status--", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.AppStatus, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
I usually use this syntax, especially if I want to update only certain properties
public ActionResult Edit( Application application)
{
if (ModelState.IsValid)
{
var existedApplication=db.Applications.FirstOrDefault(i=> i.ApplicationID=application.ApplicationID);
if(existedApplication!=null)
{
existedApplication.AppStatus = application.ApplicationStatus;
db.Entry(existedApplication).Property(i => i.ApplicationStatus).IsModified = true;
var result = db.SaveChanges();
}
.....
Serge's answer pretty much fixed my issue- but I did need to make a couple of adjustments to get it to run.
public ActionResult Edit(Application application)
{
if (ModelState.IsValid)
{
var existedApplication = db.Applications.FirstOrDefault(i => i.ApplicationID == application.ApplicationID);
if (existedApplication != null)
{
existedApplication.AppStatus = application.AppStatus;
db.Entry(existedApplication).Property(i => i.AppStatus).IsModified = true;
var result = db.SaveChanges();
return RedirectToAction("Index");
}
return View(application);
}
return View(application);
}
Related
FYI, I am also populating a field model.VdiId from a partial page using jQuery. When I am clicking the submit button of the form page, It is redirecting me to a blank page, instead of hitting the BookVdi action of Booking controller. I am throwing exception and putting breakpoints which are not hitting as well.
Below is the View Code:
#model HVDI.Models.BookingViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (#Html.BeginForm("BookVdi", "Booking", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<h4>Booking</h4>
<hr />
<div class="form-inline">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.VdiId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.VdiId, new { htmlAttributes = new { #class = "form-control disabled" } })
#Html.ValidationMessageFor(model => model.VdiId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.BookingDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.BookingDate, new { htmlAttributes = new { #class = "form-control datepicker" } })
#Html.ValidationMessageFor(model => model.BookingDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TimeStart, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TimeStart, new { htmlAttributes = new { #class = "form-control timepicker" } })
#Html.ValidationMessageFor(model => model.TimeStart, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TimeEnd, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TimeEnd, new { htmlAttributes = new { #class = "form-control timepicker" } })
#Html.ValidationMessageFor(model => model.TimeEnd, "", new { #class = "text-danger" })
</div>
</div>
<div class="showing">
<input type="button" class="align-content-end" onclick="" id="btnSearch" value="Search" />
</div>
</div>
<br />
<div class="form-group">
<div class="table-dark" id="vdiSection">
</div>
</div>
<br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Book" class="btn btn-default" />
</div>
</div>
}
Below is the Controller code:
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public void BookVdi(BookingViewModel bookingDetails)
{
if (!ModelState.IsValid)
{
throw new Exception();
}
Booking newBooking = new Booking();
newBooking.BookingDate = bookingDetails.BookingDate; //I am putting a breakpoint here
}
It is opening a blank page:
http://localhost:61110/Booking/BookVdi
You see empty page because you might not return anything when action executed properly. Please change your code as per below.
[HttpPost]
[ValidateAntiForgeryToken]
public void BookVdi(BookingViewModel bookingDetails)
{
if (!ModelState.IsValid)
{
ViewBag,Error = "Please, correct all errors.";
return View("YOURVIEWNAMEFOR FORM");
}
Booking newBooking = new Booking();
newBooking.BookingDate = bookingDetails.BookingDate;
// Write save code here
return RedirectToAction("Index"); // you must redirect to something when action executed.
}
Also add this ViewBag to see errors on view
#Html.AntiForgeryToken()
#Html.ValidationSummary()
#ViewBag.Error // add this line
<h4>Booking</h4>
Also look at this question if you need specific model errors to be shown in view question
I cannot figure out why my view only passes back a NULL for a model to my controller.
This is for an Edit Post method. I checked other controllers with Edit Post methods that are structured the same way as this one and they work fine. It seems to be just this view and controller.
Here is my view:
#model Non_P21_Quote_System_v1._0.Models.gl_code
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#if (TempData["Message"] != null)
{
<div style="color:green">
#TempData["Message"]
</div><br />
}
#if (ViewBag.error != null)
{
<div style="color:red">
<h3>#ViewBag.error</h3>
</div><br />
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>gl_code</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.GL_code, "GL Code", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_code, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_code, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.GL_description, "Gl Description", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.expense_type_ID, "Expense", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("expense_type_ID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.expense_type_ID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.eag, "Employee Account Group", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("eag", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.eag, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "gl_Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here is my controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "ID,GL_code,GL_description,expense_type_ID,eag")] gl_code gl_code)
{
if (ModelState.IsValid)
{
db.Entry(gl_code).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("gl_Index");
}
ViewBag.eag = new SelectList(db.employee_account_group, "ID", "eag_name");
ViewBag.expense_type_ID = new SelectList(db.expense_type, "ID", "type", gl_code.expense_type_ID);
return View(gl_code);
}
When I debug it, I see the model being passed in is of value NULL. I am seeing this on the controller side at the the parameters part of the Edit method.
Its null because your model contains a property named gl_code and you have also named the parameter for your model gl_code in the POST method.
Change the name of one or the other and the model will bind correctly.
What is happening internally is that the form submits a name/value pair for each successful form control, in your case gl_code=someValue. The DefaultModelBinder first initializes a new instance of your model. It then reads the form values and finds a match for the property in your model and sets it to someValue. But it also finds a match in the method parameters and tries set the value of the parameter to someValue, which fails (because you cannot do gl_code gl_code = "someValue";) and the model becomes null.
It appears you have a property on your view model called gl_code. In your controller, you also refer to the view model as gl_code.
Try changing this.
public async Task<ActionResult> Edit(gl_code gl_code)
To
public async Task<ActionResult> Edit(gl_code model)
I cannot figure out why my view only passes back a NULL for a model to my controller.
This is for an Edit Post method. I checked other controllers with Edit Post methods that are structured the same way as this one and they work fine. It seems to be just this view and controller.
Here is my view:
#model Non_P21_Quote_System_v1._0.Models.gl_code
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#if (TempData["Message"] != null)
{
<div style="color:green">
#TempData["Message"]
</div><br />
}
#if (ViewBag.error != null)
{
<div style="color:red">
<h3>#ViewBag.error</h3>
</div><br />
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>gl_code</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.GL_code, "GL Code", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_code, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_code, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.GL_description, "Gl Description", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.expense_type_ID, "Expense", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("expense_type_ID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.expense_type_ID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.eag, "Employee Account Group", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("eag", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.eag, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "gl_Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here is my controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "ID,GL_code,GL_description,expense_type_ID,eag")] gl_code gl_code)
{
if (ModelState.IsValid)
{
db.Entry(gl_code).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("gl_Index");
}
ViewBag.eag = new SelectList(db.employee_account_group, "ID", "eag_name");
ViewBag.expense_type_ID = new SelectList(db.expense_type, "ID", "type", gl_code.expense_type_ID);
return View(gl_code);
}
When I debug it, I see the model being passed in is of value NULL. I am seeing this on the controller side at the the parameters part of the Edit method.
Its null because your model contains a property named gl_code and you have also named the parameter for your model gl_code in the POST method.
Change the name of one or the other and the model will bind correctly.
What is happening internally is that the form submits a name/value pair for each successful form control, in your case gl_code=someValue. The DefaultModelBinder first initializes a new instance of your model. It then reads the form values and finds a match for the property in your model and sets it to someValue. But it also finds a match in the method parameters and tries set the value of the parameter to someValue, which fails (because you cannot do gl_code gl_code = "someValue";) and the model becomes null.
It appears you have a property on your view model called gl_code. In your controller, you also refer to the view model as gl_code.
Try changing this.
public async Task<ActionResult> Edit(gl_code gl_code)
To
public async Task<ActionResult> Edit(gl_code model)
I cannot figure out why my view only passes back a NULL for a model to my controller.
This is for an Edit Post method. I checked other controllers with Edit Post methods that are structured the same way as this one and they work fine. It seems to be just this view and controller.
Here is my view:
#model Non_P21_Quote_System_v1._0.Models.gl_code
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#if (TempData["Message"] != null)
{
<div style="color:green">
#TempData["Message"]
</div><br />
}
#if (ViewBag.error != null)
{
<div style="color:red">
<h3>#ViewBag.error</h3>
</div><br />
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>gl_code</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.GL_code, "GL Code", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_code, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_code, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.GL_description, "Gl Description", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GL_description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GL_description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.expense_type_ID, "Expense", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("expense_type_ID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.expense_type_ID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.eag, "Employee Account Group", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("eag", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.eag, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "gl_Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here is my controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "ID,GL_code,GL_description,expense_type_ID,eag")] gl_code gl_code)
{
if (ModelState.IsValid)
{
db.Entry(gl_code).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("gl_Index");
}
ViewBag.eag = new SelectList(db.employee_account_group, "ID", "eag_name");
ViewBag.expense_type_ID = new SelectList(db.expense_type, "ID", "type", gl_code.expense_type_ID);
return View(gl_code);
}
When I debug it, I see the model being passed in is of value NULL. I am seeing this on the controller side at the the parameters part of the Edit method.
Its null because your model contains a property named gl_code and you have also named the parameter for your model gl_code in the POST method.
Change the name of one or the other and the model will bind correctly.
What is happening internally is that the form submits a name/value pair for each successful form control, in your case gl_code=someValue. The DefaultModelBinder first initializes a new instance of your model. It then reads the form values and finds a match for the property in your model and sets it to someValue. But it also finds a match in the method parameters and tries set the value of the parameter to someValue, which fails (because you cannot do gl_code gl_code = "someValue";) and the model becomes null.
It appears you have a property on your view model called gl_code. In your controller, you also refer to the view model as gl_code.
Try changing this.
public async Task<ActionResult> Edit(gl_code gl_code)
To
public async Task<ActionResult> Edit(gl_code model)
I'm doing a form that contains a List<object>. This List<object> must be sent to the controller but I don't want to use JSON. Is it possible? Do I have to test with id="MyField[i]" or anything like that?
Here is the Razor code that is targeted:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Ajout de Critères sur l'audit #Model.idAudit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#foreach (var item in Model.criteresList)
{
<div class="form-group">
#Html.LabelFor(model => item.nomCritere, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => item.nomCritere, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => item.nomCritere, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => item.libelle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => item.libelle, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => item.libelle, "", new { #class = "text-danger" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
And the Controller
[HttpPost]
public ActionResult Criteres(CritereViewModel model)
{
// Call BL to save them all
return RedirectToAction("Index");
}
Well this article explains everyting about binding arrays in MVC
If you want to post your form values with bare hands you need to do something like this:
#for (var i = 0 ;i < in Model.criteresList.Count();i++)
{
#Html.EditorFor(model => Model.criteresList[i].nomCritere, new { htmlAttributes = new { #class = "form-control" } })
}
the previous answer is viable and what you say also works (id = "MyField [i]") you just have to add the object properties.
#Html.Hidden(string.Format("model.criteresList[{0}].nomCritere", index),someValue)
<select name="model.criteresList[#index].nomCritere" value="someValue">
in controller
[HttpPost]
public ActionResult Criteres(CritereViewModel model)
{}