I have an input table in my website which is connected to a View Model. In the controller method, I pass this View Model to the controller and vice versa, meaning the controller populates the view model with data from the data base and the view returns a view model populated with form data the user might have entered.
The problem is that once the view received the view model object, the "ID" attribute from the database is no longer there. When the Post method is called, there is no way to know which database entry must be updated.
My question is: How do I update a specific database entry when I pass a view model to the controller method?
Example Controller Method:
[HttpPost]
public ActionResult method(ViewModel vm)
{
DataContext.Context.Where(x => x.ID == vm.Object.ID) // this is where vm.Object.ID always returns "0", not the actual ID from the database entry
Context.SaveChanges();
return View(vm);
}
If you need more information, please let me know. Also, using jquery is not a viable option for this project. Thanks a lot for your help!
Edit:
View:
@model MyANTon.ViewModels.Q4_Answer_VM
@{
ViewBag.Title = "myANTon Anforderungserfassung";
ViewBag.HideNavBar = false;
}
@using (Html.BeginForm())
{
<div class="container">
<div class="jumbotron">
<hr />
<table class="grid" id="datatable">
<tr>
<th>Nr.</th>
<th>Last</th>
<th>Quelle</th>
<th>Ziel</th>
<th>Frequenz [/h]</th>
<th>Abstand [m]</th>
<th></th>
<th></th>
@{int i = 1; }
@for (var a = 0; a < Model.Matrix.Count; a++)
{
<tr>
<td>@(i++)</td>
<td>@Html.TextBoxFor(x => Model.Matrix[a].Load)</td>
<td>@Html.TextAreaFor(x => Model.Matrix[a].Source)</td>
<td>@Html.TextAreaFor(x => Model.Matrix[a].Goal)</td>
<td>@Html.TextAreaFor(x => Model.Matrix[a].Frequency)</td>
<td>@Html.TextAreaFor(x => Model.Matrix[a].Distance)</td>
<td><input type="submit" name="+" class="btn btn-default" value="+" /></td>
<td><input type="submit" class="btn btn-default" value="-" /></td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" name="Speichern" value="Speichern" />
<input type="submit" class="btn btn-default" value="Speichern und weiter" />
<input type="button" class="btn btn-default" value="Weiter zu Schritt 5" onclick="@("window.location.href='" + @Url.Action("Q_Fifthpage", "Home") + "'");" />
</div>
</div>
</div>
</div>
}
GET Method:
[HttpGet]
public ActionResult Q_FourthPage()
{
// get current Questionnaire ID
int CurrentQstID = Convert.ToInt32(Session["qstid"]);
// create vm object. Capacity is a column in the table.
var Q4ViewModel = new ViewModels.Q4_Answer_VM();
// look for existing input data columns for this questionnaire in db
if (db.Capacities.Any(x => x.Questionnaire_ID == CurrentQstID))
{
// answers exist
Q4ViewModel.Matrix.AddRange(db.Capacities.Where(x => x.Questionnaire_ID == CurrentQstID));
}
else
{
// new capacity matrix
Q4ViewModel.TMatrix = db.QuestionTexts.Where(x => x.ID == 21).FirstOrDefault();
Q4ViewModel.Matrix = new List<Models.Capacity>();
}
var tmpcapacity = new Models.Capacity();
tmpcapacity.Questionnaire_ID = Convert.ToInt32(Session["qstid"]);
Q4ViewModel.Matrix.Add(tmpcapacity);
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
return View(Q4ViewModel);
}
POST Method:
[HttpPost]
public ActionResult Q_FourthPage(ViewModels.Q4_Answer_VM vm)
{
int currentQst = Convert.ToInt32(Session["qstid"]);
if (Request.Form["+"] != null)
{
var tmpcapacity = new Models.Capacity();
tmpcapacity.Questionnaire_ID = currentQst;
vm.Matrix.Add(tmpcapacity);
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
return View(vm);
}
if (Request.Form["Speichern"] != null)
{
// save data
if (!ModelState.IsValid) return View("~/Views/Shared/Error.cshtml");
var tmpcapacity = new Models.Capacity();
for (var a = 0; a < vm.Matrix.Count; a++)
{
var current = vm.Matrix[a];
current.ID = vm.Matrix[a].ID;
if (db.Capacities.Any(x => x.ID == current.ID))
// if clause never triggers true
// vm does not contain capacity ID
{
// column exists and is changed (or not)
tmpcapacity.Distance = vm.Matrix[a].Distance;
tmpcapacity.Frequency = vm.Matrix[a].Frequency;
tmpcapacity.Source = vm.Matrix[a].Source;
tmpcapacity.Goal = vm.Matrix[a].Goal;
tmpcapacity.Load = vm.Matrix[a].Load;
Models.Capacity c = db.Capacities.Where(x => x.ID == current.ID).FirstOrDefault();
c = tmpcapacity;
db.SaveChanges();
}
else
{
// new column
tmpcapacity.Distance = vm.Matrix[a].Distance;
tmpcapacity.Frequency = vm.Matrix[a].Frequency;
tmpcapacity.Source = vm.Matrix[a].Source;
tmpcapacity.Goal = vm.Matrix[a].Goal;
tmpcapacity.Load = vm.Matrix[a].Load;
tmpcapacity.Questionnaire_ID = currentQst;
db.Capacities.Add(tmpcapacity);
db.SaveChanges();
}
}
db.SaveChanges();
}
return View(vm);
}
If you need to bind the ID to the Model then you need to use hidden filed under the form when you are using Razor.
@Html.HiddenFor(model => model.Id)
For more details
@using (Html.BeginForm("method", "ControllerName", FormMethod.Post))
{
@Html.HiddenFor(Model=>Model.ID)
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Button" />
</div>
</div>
}
Now you can access ID at your controller action method.