Implementing scenarios for adding, deleting, and updating
Open the MVCORama application that you created in the preceding example. In this procedure, you use the other table from the database—the DotNetLinks table. The previous example listed only the contents of the model.
Create a model for the DotNetLinks table. As in the last exercise, add a LINQ to SQL class for the DotNetLinks table. Right-click the Models folder, and click Add New Item. In the Visual Studio Add New Item dialog box, click the Data tab and select LINQ To SQL. Name the LINQ to SQL class DotNetLinks. Drag the DotNetLinks table from Server Explorer onto the design surface of the LINQ to SQL class. Visual Studio will create a wrapper class named DotNetLink.
As with the DotNetReferences, create a data manager for the DotNetLinks table. Right-click the Models folder, and click Add New Item. Select Class from the palette and name the class DotNetLinksManager. Visual Studio will create the class for you.
Add an instance of the DotNetLinksDataContext to the DotNetLinksManager class. Add methods to get all the rows from the table, to find a DotNetLink in the DotNetLinksDataContext, to add a DotNetLink to the DotNetLinksDataContext, and to delete a DotNetLink from the DotNetLinksDataContext. Finally, add a method to commit the changes to the underlying table. You exercise these methods through the controller:
public class DotNetLinksManager { DotNetLinksDataContext dataContext = new DotNetLinksDataContext(); public IQueryable<DotNetLink> GetAllLinks() { return dataContext.DotNetLinks; } public DotNetLink Find(int id) { DotNetLink dotNetLink; dotNetLink = dataContext.DotNetLinks.SingleOrDefault(l => l.ID == id); return dotNetLink; } public void Add(DotNetLink dotNetLink) { dataContext.DotNetLinks.InsertOnSubmit(dotNetLink); } public void Delete(DotNetLink dotNetLink) { dataContext.DotNetLinks.DeleteOnSubmit(dotNetLink); } public void Save() { dataContext.SubmitChanges(); } }
Add a DotNetLinks controller to the Controllers folder. Right-click the Controllers folder, and click Add, Controller. Visual Studio will create the controller class for you. Add an instance of the DotNetLinksManager class as a member variable of the controller and instantiate it.
public class DotNetLinksController : Controller { DotNetLinksManager dotNetLinksManager = new DotNetLinksManager(); // more code }
Add a new folder to the Views folder and name it DotNetLinks. Add a new view to that folder named Index.aspx by right-clicking the Views\DotNetLinks folder and clicking Add, View. Make it strongly typed to the DotNetLink class, and have it display the links as a list.
Now go back to the DotNetLinksController and have the Index action method create a new index view based on a list of all the available links (call the DotNetLinksManager.GetAllLinks method to do this). Note that this method catches all exceptions so that it runs cleanly. Another strategy is to let the exception propagate through the pipeline:
public ActionResult Index() { try { var dotNetLinks = dotNetLinksManager.GetAllLinks().ToList(); return View("Index", dotNetLinks); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Open the Index.aspx file and tailor the presentation to show the URLs as links that can be navigated. Changing this code causes the page to display the URL as a functional link. Locate the code that displays the URL column and change it from the following:
<td> <%= Html.Encode(item.URL) %> </td>
to this:
<td> <a href="<%= item.URL %>" > <%= Html.Encode(item.DisplayName) %> </a> </td>
Open the master page and add a new tab to the menu in the Site.Master file to show the DotNetLinks information:
<ul id="menu"> <li><%= Html.ActionLink("Home", "Index", "Home")%></li> <li><%= Html.ActionLink("DotNetReferences", "Index", "DotNetReferences")%></li> <li><%= Html.ActionLink("Dot Net Links", "Index", "DotNetLinks")%></li> <li><%= Html.ActionLink("About", "About", "Home")%></li> </ul>
Run the program and navigate to the DotNetLinks page. You should now see the links displayed as typical, functional HTTP links, as shown in the following graphic:
Now handle the details scenario—where the user can see some more detailed information about the link. Add the Details view by right-clicking the DotNetLinks\Views folder and clicking Add, View. Use the Add View dialog box to configure a strongly typed view based on the DotNetLink class, and select Details from the View Content combo box. Visual Studio will create a view based on the DotNetLink class. Make sure the name of the view file is Details.aspx.
Now you need to tell MVC how to respond to requests for the details of a particular link. Add a public method to the DotNetLinksController class to return a view to the details of a single link. Call the DotNetLinksManager.Find method using the ID passed in to the method. The ID parameter is actually passed as a URL parameter and packaged into a managed type by the MVC framework. The index view generated by Visual Studio includes a Details navigation link for items displayed in the model. After finding the specific link in the DotNetLinksManager, call the controller's View method, passing in the string "Details" as the first parameter and the DotNetLink as the second parameter:
// Get the details for a single link and show them: public ActionResult Details(int id) { try { DotNetLink dotNetLink = dotNetLinksManager.Find(id); if (dotNetLink != null) { return View("Details", dotNetLink); } else { return View(); } } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Use the new tab to navigate to the DotNetLinks index page, and select one of the links to show the details. You should see the following in the browser:
Now handle adding new entries to the database. Start by adding a new strongly typed view to the Views\DotNetLinks folder. Right-click the Views\DotNetLinks folder and click Add, View. Use the resulting dialog box to configure a strongly typed view based on the DotNetLink class. Then, select Create from the View Content combo box. Name the view Create.
Now add some methods to the controller to support adding entries. First, write a method named Create. It should simply display the default view. The following code displays the default Create view with text boxes awaiting input for the DotNetLink properties:
public ActionResult Create() { return View(); }
Add a static helper method named DotNetLinkFromFormsCollection that takes a single FormCollection as a parameter and have it return a DotNetLink. The FormCollection class is a name/value collection representing the contents of a postback. Use it to populate the DotNetLink:
private static DotNetLink DotNetLinkFromFormsCollection(FormCollection collection) { DotNetLink dotNetLink = new DotNetLink(); dotNetLink.DisplayName = collection["DisplayName"]; dotNetLink.URL = collection["URL"]; return dotNetLink; }
Add a method named Create that takes a FormCollection as the first parameter and returns an ActionResult. Apply the AcceptVerbs attribute using the HttpVerbs.Post enumeration. This helps the MVC framework to process the postback. The MVC framework will populate the FormCollection using the results of the postback. Use the DotNetLinkFromFormsCollection helper method to populate an instance of the DotNetLink class. Use the DotNetLinksManager.Add method to add the DotNetLink to the collection, and then call the DotNetLinksManager.Save method to commit the change to the underlying database. Note that this is not production code, and doesn't validate user input. A production application probably should check input to avoid bad input that might cause errors or even security attacks:
// Create scenario [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(FormCollection collection) { try { DotNetLink dotNetLink = DotNetLinkFromFormsCollection(collection); if (dotNetLinksManager. Find(dotNetLink.ID) == null) { dotNetLinksManager.Add(dotNetLink); dotNetLinksManager.Save(); } return RedirectToAction("Index"); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Run the application and try adding a new link to the collection of DotNetLinks. For example, try clicking the Create New link. When the Create page opens, type in a display name such as MSDN. Then, type http://msdn.microsoft.com in the URL field and some comments in the information field.
Now create a view to handle the edit scenario. Right-click the Views\DotNetLinks folder and add a new strongly typed view based on the DotNetLink class. Select Edit in the View Content combo box. Visual Studio will generate a new view useful for editing existing entries.
Add a method to the controller for handling editing. It should take a single integer parameter representing the ID of the item to edit. The MVC framework will call this method in the controller when you navigate to the Edit page (you can do this by going to the DotNetLinks home page and clicking the Edit link for one of the entries). Use the DotNetLinksManager.Find method to get the DotNetLink specified by the ID. Then, call the controller View method, passing the string "Edit" (to invoke the Edit view) and a reference to the DotNetLink retrieved from the DotNetLinksManager:
// handle editing... public ActionResult Edit(int id) { try { DotNetLink dotNetLink = dotNetLinksManager.Find(id); if (dotNetLink != null) { return View("Edit", dotNetLink); } return View(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Add a method to the controller for handling the postback. Name the method Edit and have it take two parameters: an integer specifying the ID of the link being edited, and a FormCollection. Have the Edit method return an ActionResult and use the AcceptVerbs attribute to specify this method is a response to a postback. Use the DotNetLinksManager.Find method to get the DotNetLink specified by the ID. Use the controller's base class method named UpdateModel to populate the DotNetLink from the collection (UpdateModel is part of the framework and automatically updates the model). Then, call the DotNetLinkManager.Save method to save the information to the database:
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(int id, FormCollection collection) { try { DotNetLink dotNetLink = dotNetLinksManager.Find(id); UpdateModel(dotNetLink); dotNetLinksManager.Save(); return RedirectToAction("Index"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Finally, handle the delete scenario. Add a strongly typed view based on the DotNetLinks class to the Views\DotNetLinks folder and name it Delete. Make it empty by selecting Empty in the View Content combo box. This will be the confirmation page. Add some text to the content area that asks the user to confirm that the record should be deleted. Add an HTML form to the page by calling Html.BeginForm. Include a Submit button in the form. Clicking this button will cause a postback:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MVCORama.Models.DotNetLink>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Delete </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2> Confirm Delete </h2> <div> <p>Do you want to delete this link?: <i> <%=Html.Encode(Model.DisplayName) %>? </i> </p> </div> <% using (Html.BeginForm()) { %> <input name="confirmButton" type="submit" value="Delete" /> <% } %> </asp:Content>
Open the Index.aspx page (the index view for the DotNetLinks). Locate the section of code that iterates through the items and include a Delete action (put it along with the existing links to get the item's details and to edit the item):
<% foreach (var item in Model) { %> <tr> <td> <%= Html.ActionLink("Edit", "Edit", new { id=item.ID }) %> | <%= Html.ActionLink("Details", "Details", new { id=item.ID })%> | <%= Html.ActionLink("Delete", "Delete", new { id=item.ID})%> </td> <td> <%= Html.Encode(item.DisplayName) %> </td> <td> <a href="<%= item.URL %>" > <%= Html.Encode(item.DisplayName) %> </a> </td> </tr> <% } %>
Now add some methods to the controller for deleting a specific DotNetLink record. First, add a single method named Delete that takes a single parameter of type integer. The method should return an ActionResult. This is the method for responding to the delete GET request. Use the DotNetLinksManager.Find method to get a reference to the DotNetLink represented by the ID. Then, call the controller's View method, passing in the string "Delete" and the reference to the DotNetLink. This will show the delete confirmation page:
// Methods for deleting public ActionResult Delete(int id) { try { DotNetLink dotNetLink = dotNetLinksManager.Find(id); if (dotNetLink != null) { return View("Delete", dotNetLink); } else { return View(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Finally, add a method named Delete to the controller that takes an integer and a FormCollection. Adorn the method using the AcceptVerbs attribute and pass in the HttpVerbs.Post enumeration so that this method is called during postbacks. This method will be called when users click the delete confirmation button. Use the DotNetLinksManager.Find method to locate the specific DotNetLink based on the ID passed in to the controller. Then, call the DotNetLinksManager.Delete and the DotNetLinksManager.Save methods to remove the record from the database. Use the controller's RedirectToAction method to show the index view:
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Delete(int id, FormCollection formsCollection) { try { DotNetLink dotNetLink = dotNetLinksManager.Find(id); if (dotNetLink != null) { dotNetLinksManager.Delete(dotNetLink); dotNetLinksManager.Save(); return RedirectToAction("Index"); } else { return View(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return View(); } }
Now run the program and try deleting one of the links.