Reputation: 677
I've been following the nerd dinner tutorial. I branched off to create my own project based on it and I'm having trouble getting UpdateModel to work. It seems pretty complicated so I'll try to explain it in as much detail below.
In ServersController.cs:
public ActionResult Create(FormCollection formValues)
{
Server server = new Server();
try
{
UpdateModel(server); <----- This is not working
// server.Name = "testAdd";
// server.OS = "2008 R2";
serverRepository.Add(server);
serverRepository.Save();
return RedirectToAction("Details", new { id = server.ServerID });
}
catch
{
**not important**
}
If I try to use UpdateModel(Server), nothing gets saved to the database table. However, If I comment out that line, and use the commented lines in the code that sets the server.Name and server.OS, this DOES save it to the table. However, any other form input that's being posted doesn't save..
For example, if I explicitly set the server.Name and server.OS as in the code above, but then set other properties such as LastBackedUp and Model through the form, none of the properties set from the form are saved to the database table, nor are they reflected in the "details" view when the page gets redirected.
Here's the code for the "Details" GET method, also in ServersController.cs:
public ActionResult Details(int id)
{
Server server = serverRepository.GetServer(id);
RackInfo rackInfo = rackRepository.GetRack(id);
if (server == null)
return View("NotFound");
else
return View(new ServerDetailViewModel(rackInfo, server));
}
Basically, After the new server is created and saved, it's supposed to load the above "Detail" view, which uses a "ServerDetailViewModel" class to generate some data to pass to the view.
Here's the ServerDetailViewModel() code:
public class ServerDetailViewModel
{
public RackInfo RackInfo { get; private set; }
public Server Server { get; private set; }
public string Name { get; private set; }
public ServerDetailViewModel(RackInfo rackInfo, Server server)
{
Server = server;
RackInfo = rackInfo;
*more code here that sets stuff*
}
}
I think my problem has something to do with how my form parameters are passed around.
It seems odd that I can explicitly code in some server properties, and then save those into the database table. But when I try to do anything with UpdateModel, nothing seems to go through. When I use UpdateModel, it will redirect me to the details page, but none of the values I input for the properties seem to appear. Additionally, none of the properties I put in through the form gets saved to the database table.
If any of you have walked through the Nerd Dinner tutorial (http://weblogs.asp.net/scottgu/archive/2009/04/28/free-asp-net-mvc-nerddinner-tutorial-now-in-html.aspx), that's what I've been using. When I got through about halfway, I decided to start a new project that implemented what I was using.
I was initially able to get Create to work, but after adding another separate repository to the controller, the create method broke..
Any input or insight on this would be much appreciated. Please let me know if you need any additional information.
Thanks S.O.!!
EDIT (Including the "Create" Form):
@model PACSSL.Models.ServerFormViewModel
@using (Html.BeginForm())
{ @Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Server</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.Server.Name, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.Name)
@Html.ValidationMessageFor(model => model.Server.Name)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Domain, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.Domain, Model.Domain)
@Html.ValidationMessageFor(model => model.Domain)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.BackedUp, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.BackedUp, Model.BackedUp)
@Html.ValidationMessageFor(model => model.BackedUp)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.Role, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.Role)
@Html.ValidationMessageFor(model => model.Server.Role)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.GroupOwner, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.GroupOwner)
@Html.ValidationMessageFor(model => model.Server.GroupOwner)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.PatchNotes, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.PatchNotes)
@Html.ValidationMessageFor(model => model.Server.PatchNotes)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.LastPatched, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.LastPatched) Format: yyyy-mm-dd
@Html.ValidationMessageFor(model => model.Server.LastPatched)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.PatchedBy, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.PatchedBy, Model.PatchedBy)
@Html.ValidationMessageFor(model => model.PatchedBy)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.VP, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.VP, Model.VP)
@Html.ValidationMessageFor(model => model.VP)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.VMHost, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.VMHost)
@Html.ValidationMessageFor(model => model.Server.VMHost)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.Location, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.Location)
@Html.ValidationMessageFor(model => model.Server.Location)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.PurchaseDate, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.PurchaseDate) Format: yyyy-mm-dd
@Html.ValidationMessageFor(model => model.Server.PurchaseDate)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.OS, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.OS)
@Html.ValidationMessageFor(model => model.Server.OS)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.Model, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.Model)
@Html.ValidationMessageFor(model => model.Server.Model)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.DellST, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.DellST)
@Html.ValidationMessageFor(model => model.Server.DellST)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.ServiceContract, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.ServiceContract) Format: yyyy-mm-dd
@Html.ValidationMessageFor(model => model.Server.ServiceContract)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Server.IsLive, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Server.IsLive)
@Html.ValidationMessageFor(model => model.Server.IsLive)
</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>
}
Upvotes: 0
Views: 1408
Reputation: 25221
Here's your problem.
When you generate a form field with the expression model => model.Server.OS
, you produce a field that has a 'name' attribute like this:
"Server.OS"
Then, when you try and bind it to a model, it will bind to a model property matching the expression "Server.OS" - so, if your model object is server
, it will bind to the following:
server.Server.OS
When, I presume, you want to bind to server.OS
. My suggestion would be to flatten out your view model so instead of having Model.Server.OS
you just have Model.OS
. Then when you do...
UpdateModel(server);
... it should bind the "OS" field to the "OS" property.
In short: your view model has an extra level of property nesting compared to your server
object, so there is a mismatch in the generated field names and the properties you're trying to bind to.
Better still, bind back to the same view model by modifying your controller action to have the signature:
public ActionResult Create(ServerDetailViewModel model)
And then do the mapping manually in the controller (or some static mapping class) - your view models and domain model should be completely independent.
Upvotes: 1
Reputation: 5580
You are trying to update a "empty" Model. It is empty because you are creating a new instance.
Server server = new Server();
try
{
//At this point you have an empty model because you just
//created a new Instance of your Server class,
//It is updating the Model with all Null values because they aren't assigned to anything
UpdateModel(server); <----- This is not working
//Here you assign values to your model, hence why they are not null anymore
// Your other values aren't getting saved because they still don't have a value.
server.Name = "testAdd";
server.OS = "2008 R2";
serverRepository.Add(server);
serverRepository.Save();
return RedirectToAction("Details", new { id = server.ServerID });
}
Upvotes: 0