Saturday, December 13, 2008

CSLA & ASP.NET MVC (part 2: Editing)

In the last post, we discussed about listing viewing detail for objects built with CSLA framework via ASP.NET MVC. Now in this post, I will show the editing and adding of the object - in our example here, using "Customer".

In essence, editing and adding is not that much different - one pulls an existing one and modify it with incoming data, the other is just creating a new instance and filling it up with incoming data. In our code, we try to leverage CSLA as much as possible, where the "Save" method is called only when the object is "dirty". Another thing is that we also want to get as much as data validation within the CSLA object as possible rather than manual/custom checking in our controller.

First, editing.

        [AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit(int id)
{
viewData.CurrentCustomer = Customer.GetCustomer(id);
viewData.Titles = TitleInfoList.GetTitleInfoList();
return View("~/Views/Customer/Controls/CustomerEdit.ascx", viewData);
}

The method Edit is tagged with "AcceptVerbs" attribute and scoped to only accept "Get" request. What this means is that this method can and will only be executed during a GET. Scott Guthrie has an excellent post regarding this attributes in his blog - I suggest for you to check it out.
The rest of the code is pretty simple, get the Customer record that is selected (passed in as a Id parameter in the method), put the customer instance into my viewData, get the list of titles to populate my Titles drop down, and then pass the view data into the viewer - pretty straight forward.

Now, let's take a look at the handler code for edit submission/post - for when somebody hit submit on our edit form.
        [ActionName("Edit"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(int id)
{
try
{
Customer customer = Customer.GetCustomer(id);
UpdateModel(customer, Request.Form.AllKeys);
if (!customer.IsValid)
{
foreach (Csla.Validation.BrokenRule brokenRule in customer.BrokenRulesCollection)
{
ModelState.SetAttemptedValue(brokenRule.Property, customer.GetType().GetProperty(brokenRule.Property).GetValue(customer, null).ToString());
ModelState.AddModelError(brokenRule.Property, brokenRule.Description);
}
viewData.CustomerList = CustomerInfoList.GetCustomerInfoList();
viewData.Titles = TitleInfoList.GetTitleInfoList();
viewData.CurrentCustomer = customer;
viewData.ValidationErrorFlag = true;
return View("~/Views/Customer/Controls/CustomerEdit.ascx",viewData);
}
else
{
if (customer.IsDirty)
{
customer.DateUpdated = DateTime.Now;
customer.Save();
TempData["Message"] = "I saved " + customer.LastName + " to the database";
}
else
{
TempData["Message"] = "There were no changes to be made";
}
return null;
}
}
catch (Exception ex)
{
TempData["ErrorMessage"] = ex.Message;
return RedirectToAction("Edit", new { id = id });
}
}

OK, that seems long - so let's examine the code bit by bit so we can fully understand what is going on. If you notice, above the method name, there is also an "AcceptVerb" attribute, but this time it is set to "POST". Another thing is that it has ActionName("Edit") - which when combined with the AcceptVerb attribute, it will have the meaning that if there is an action "Edit" called on the controller with a POST, it will then get redirected into this method "Update".
        
try
{
Customer customer = Customer.GetCustomer(id);
UpdateModel(customer, Request.Form.AllKeys);
...

The 2 lines of code above, it is basically getting the Customer record being edited, and then call "UpdateModel" method, where it tries to match the instance of Customer called "customer" with the data submitted into the Request.Form.
                if (!customer.IsValid)
{
foreach (Csla.Validation.BrokenRule brokenRule in customer.BrokenRulesCollection)
{
ModelState.SetAttemptedValue(brokenRule.Property, customer.GetType().GetProperty(brokenRule.Property).GetValue(customer, null).ToString());
ModelState.AddModelError(brokenRule.Property, brokenRule.Description);
}
viewData.Titles = TitleInfoList.GetTitleInfoList();
viewData.CurrentCustomer = customer;
viewData.ValidationErrorFlag = true;
return View("~/Views/Customer/Controls/CustomerEdit.ascx",viewData);
}
...

Here we check whether the instance filled with data from the form is valid based on the rules specified in the CSLA rules. If it is not valid then iterate the broken rules collection and set the model marked with error messages appropriately. Now after the model is marked with the error, we want to display back the form with the model so the error messages can be displayed accordingly field by field. To do this, we then populate the Titles field in our viewdata back with list of available titles, and also populating our CurrentCustomer property with the edited customer, set the error flag and return the viewer for "Edit".
                else
{
if (customer.IsDirty)
{
customer.DateUpdated = DateTime.Now;
customer.Save();
TempData["Message"] = "I saved " + customer.LastName + " to the database";
}
else
{
TempData["Message"] = "There were no changes to be made";
}
return null;
}
...

If the instance does not violate any rules, then we can save if necessary. Even though CSLA will detect automatically whether the instance is dirty or not before saving to the database, but we chose to check in our controller so we then can display whether there are data being saved or not. Since there is nothing else needed to be done after the saving, we then return null value.


Related Posts:
ASP.NET MVC, AJAX, jQuery
CSLA & ASP.NET MVC (part 1)
CSLA & ASP.NET MVC (part 3)

No comments: