CRUD Operations w/ XML Data in ASP.NET MVC 3
— ASP.NET
Create, read, update, and delete (CRUD) operations are easy to perform in ASP.NET MVC. By using the default T4 scaffolding templates and strongly-typed views you can quickly build a web application that can create, update, and delete records.
Using ASP.NET MVC 3 I built a simple Billing Application that performs CRUD operations on an XML file. With LINQ I was able to quickly write code to update nodes in the XML file.
How my app works
My ASP.NET MVC 3 Billing App uses the Repository pattern for accessing data. The advantages with this pattern are it makes my methods easier to maintain and limits the amount of duplicated code in the data access layer. For each CRUD operation I have a method in my repository.
Model
I created a Billing.cs class based on the structure of my Billings.xml file.
1<?xml version="1.0" encoding="utf-8"?>2<billing>34 <item>5 <id>1</id>6 <customer>Vandelay Industries</customer>7 <type>Development</type>8 <date>4/5/2011</date>9 <description>Update jQuery animation on Intranet homepage</description>10 <hours>2</hours>11 </item>12 <item>13 <id>2</id>14 <customer>Dunder Mifflin</customer>...
In my Billing.cs class I used the System.ComponentModel.DataAnnotations namespace for adding validation to my model properties.
1public class Billing2{3 public Billing()4 {5 this.ID = 0;6 this.Customer = null;7 this.Type = null;8 this.Date = DateTime.Now;9 this.Description = null;10 this.Hours = 0;11 }1213 public Billing(int id, string customer, string type, DateTime date, string description, int hours)14 {15 this.ID = id;16 this.Customer = customer;17 this.Type = type;18 this.Date = date;19 this.Description = description;20 this.Hours = hours;21 }2223 public int ID { get; set; }24 [Required(ErrorMessage = "Customer is required")]25 public string Customer { get; set; }26 [Required(ErrorMessage = "Type is required")]27 public string Type { get; set; }28 [Required(ErrorMessage = "Date is required")]29 public DateTime Date { get; set; }30 [Required(ErrorMessage = "Description is required")]31 public string Description { get; set; }32 [Required(ErrorMessage = "Hours is required")]33 public int Hours { get; set; }34}
My model folder also contains my interface IBillingRepository.cs.
1public interface IBillingRepository2{3 IEnumerable<Billing> GetBillings();4 Billing GetBillingByID(int id);5 void InsertBilling(Billing billing);6 void DeleteBilling(int id);7 void EditBilling(Billing billing);8}
And all the heavy lifting is done by my BillingRepository.cs class.
1public class BillingRepository: IBillingRepository2{3 private List<Billing> allBillings;4 private XDocument billingData;56 // constructor7 public BillingRepository()8 {9 allBillings = new List<Billing>();1011 billingData = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Billings.xml"));12 var billings = from billing in billingData.Descendants("item")13 select new Billing((int)billing.Element("id"), billing.Element("customer").Value,14 billing.Element("type").Value, (DateTime)billing.Element("date"),15 billing.Element("description").Value, (int)billing.Element("hours"));16 allBillings.AddRange(billings.ToList<Billing>());17 }1819 // return a list of all billings20 public IEnumerable<Billing> GetBillings()21 {22 return allBillings;23 }2425 public Billing GetBillingByID(int id)26 {27 return allBillings.Find(item => item.ID == id);28 }2930 // Insert Record31 public void InsertBilling(Billing billing)32 {33 billing.ID = (int)(from b in billingData.Descendants("item") orderby (int)b.Element("id") descending select (int)b.Element("id")).FirstOrDefault() + 1;3435 billingData.Root.Add(new XElement("item", new XElement("id", billing.ID), new XElement("customer", billing.Customer),36 new XElement("type", billing.Type), new XElement("date", billing.Date.ToShortDateString()), new XElement("description", billing.Description),37 new XElement("hours", billing.Hours)));3839 billingData.Save(HttpContext.Current.Server.MapPath("~/App_Data/Billings.xml"));40 }4142 // Delete Record43 public void DeleteBilling(int id)44 {45 billingData.Root.Elements("item").Where(i => (int)i.Element("id") == id).Remove();4647 billingData.Save(HttpContext.Current.Server.MapPath("~/App_Data/Billings.xml"));48 }4950 // Edit Record51 public void EditBilling(Billing billing)52 {53 XElement node = billingData.Root.Elements("item").Where(i => (int)i.Element("id") == billing.ID).FirstOrDefault();5455 node.SetElementValue("customer", billing.Customer);56 node.SetElementValue("type", billing.Type);57 node.SetElementValue("date", billing.Date.ToShortDateString());58 node.SetElementValue("description", billing.Description);59 node.SetElementValue("hours", billing.Hours);6061 billingData.Save(HttpContext.Current.Server.MapPath("~/App_Data/Billings.xml"));62 }63}
Controller
I have one Controller named BillingController.cs which interacts with my repository and contains get and post requests for performing CRUD operations. By using the ModelState.AddModelError() method I am able to display Exception error messages in red font for any failed CRUD operations.
1public class BillingController : Controller2{3 private IBillingRepository _repository;4 private SelectList typeList = new SelectList(new[]{"Meeting","Requirements","Development","Testing","Documentation"});56 public BillingController(): this(new BillingRepository())7 {8 }910 public BillingController(IBillingRepository repository)11 {12 _repository = repository;13 }141516 public ActionResult Index()17 {18 return View(_repository.GetBillings());19 }202122 public ActionResult Details(int id)23 {24 Billing billing = _repository.GetBillingByID(id);25 if (billing == null)26 return RedirectToAction("Index");2728 return View(billing);29 }303132 public ActionResult Create()33 {34 ViewBag.TypeList = typeList;35 return View();36 }373839 [HttpPost]40 public ActionResult Create(Billing billing)41 {42 if (ModelState.IsValid)43 {44 try45 {46 _repository.InsertBilling(billing);47 return RedirectToAction("Index");48 }49 catch(Exception ex)50 {51 //error msg for failed insert in XML file52 ModelState.AddModelError("", "Error creating record. " + ex.Message);53 }54 }5556 return View(billing);57 }585960 public ActionResult Edit(int id)61 {62 Billing billing = _repository.GetBillingByID(id);63 if (billing == null)64 return RedirectToAction("Index");6566 ViewBag.TypeList = typeList;67 return View(billing);68 }697071 [HttpPost]72 public ActionResult Edit(Billing billing)73 {74 if (ModelState.IsValid)75 {76 try77 {78 _repository.EditBilling(billing);79 return RedirectToAction("Index");80 }81 catch (Exception ex)82 {83 //error msg for failed edit in XML file84 ModelState.AddModelError("", "Error editing record. " + ex.Message);85 }86 }8788 return View(billing);89 }909192 public ActionResult Delete(int id)93 {94 Billing billing = _repository.GetBillingByID(id);95 if (billing == null)96 return RedirectToAction("Index");97 return View(billing);98 }99100101 [HttpPost]102 public ActionResult Delete(int id, FormCollection collection)103 {104 try105 {106 _repository.DeleteBilling(id);107 return RedirectToAction("Index");108 }109 catch(Exception ex)110 {111 //error msg for failed delete in XML file112 ViewBag.ErrorMsg = "Error deleting record. " + ex.Message;113 return View(_repository.GetBillingByID(id));114 }115 }116}
View
My Views are standard since they use the default T4 scaffolding templates (Create, Delete, Details, Edit, and List). One thing I added to my Create and Edit Views was the jQuery UI date picker. With ASP.NET MVC the Scripts and Content folders come with the jQuery UI base theme installed by default. To utilize jQuery UI I added an optional RenderSection to my layout master page in the head for adding the jQuery UI stylesheet to specific Views. All I had to do was add the jQuery UI stylesheet and JavaScript code to the Create and Edit Views to get the date picker up and running.
_Layout.cshtml optional RenderSection tag
1<html>2<head>3 <title>@ViewBag.Title</title>4 <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />567 @RenderSection("Header", false)89 <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>10</head>11</pre>12<em>Create.cshtml</em> JavaScript and CSS for Date Picker13<pre lang="html4strict">14@section Header{15 <link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" rel="stylesheet" type="text/css" />16}1718<h2>Create</h2>19<script src="@Url.Content("~/Scripts/jquery-ui.min.js")" type="text/javascript"></script>20<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>21<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>2223<script type="text/javascript">24 $(function () {25 $("#Date").datepicker();26 });27</script>
Conclusion
ASP.NET MVC makes it easy to perform CRUD operations on data. The T4 Scaffolding templates create forms and lists for your strongly-typed Views so you don't have to waste time manually coding them yourself. These templates also provide form validation which speeds up the development process. ASP.NET MVC comes with some great tools to get your web apps up and running very quickly.