Вы находитесь на странице: 1из 16

What is MVC Framework?

Model View Controller is an architecture or framework for building applications that separate the data (model) from the user interface (view) and the processing (controller). Providing a programming interface between the data and the processing has been a primary concept in information technology for decades. MVC is widely used in Web-based application frameworks, not just ASP.NET.

The Structure of the Solution (Project)


This is a screenshot of the sidebar of my Ms Visual Studio project. Its actually the structure of the whole project. In the App_Data folder, is the databse IS_DB. The content folder contains the site images stored in the SiteImages folder, along with the CSS style sheet. The Controllers in the project are Account, ControlPanel, Error and Home Controller. There is one Html Helper class in the Helpers folder. The Models are the AccountModels and ISDataClasses.The visible here. Then next folder is the Views folder, which contains Views of Account, ControlPanel, Error, Home and Shared. ViewModels are all

Views
This is a screenshot of the sidebar in the project showing all the existing Views.

HomeController
View Index, Profile, ContactUs Actions
public ActionResult Index() { return View(); }

This Action is used to directly display whatever is in the View page. Similar code is used to return the Profile and Contact Us pages.

View Events Action The following code is used to retrieve the records in the Events table and sort them in descending order of their start date. The sorted events are passed into the View to display them for the user.
public ActionResult Events() { var context = new ISDataClassesDataContext(); var events = from e in context.Events orderby e.StartDate descending select e; return View(events); }

View StaticPages, Event and Article Details Actions


public ActionResult StaticPage(int? id) { var context = new ISDataClassesDataContext(); var stpage = (from s in context.StaticPages where s.StaticPageID == id select s).SingleOrDefault(); return View(stpage); }

This is the code written in the HomeController to access a specific Static Page, in other words this is an action to retrieve and display a specific record in the StaticPages table. The HomeController is the primary Controller which will be used to display pages that are accessible to users who are not logged in.

Firstly as in the codes that will be written to do similar functions; a new instace of the DataContext class is created. Working primarily with a single class called the DataContext, LINQ developers can:
y y y

Connect to a database Access data Submit changes back to the server

Secondly, using a LINQ query, the required StaticPage record is selected based on its ID number. If the StaticPage record ID matches with the id parameter of this Action, it is chosen to be displayed. Then the View is returned, displaying the selected StaticPage record. Similar code is used to retrieve and display specific records in the Articles and Events tables. The Actions which are used to do so are called Article Details and Event Details respectively.

View Achievements Action


public ActionResult Achievements(int? year) { var context = new ISDataClassesDataContext(); var years = (from y in context.Achievements orderby y.AchievedDate.Year descending select y.AchievedDate.Year).Distinct(); ViewData["Years"] = years; if (year == null) year = years.FirstOrDefault(); var achievements = from a in context.Achievements where a.AchievedDate.Year == year select a; var types = (from t in achievements select t.CompetitionType.TypeName).Distinct(); ViewData["Types"] = types; return View(achievements); }

This is the code used to retrieve records from the Achievements table. Here, the years of achievements are chosen and sorted in descending order. Only distinct years are chosen i.e. even if the table contains multiple instances of the year 2002, the year 2002 will be chosen only once. This selection of years is passed into the View where it will be manipulated as needed. As already stated in the Design stage, the achievements records will first be sorted according to the Year of Achievement. Then those sorted records will be categorized by Competition Types.

So when displaying the achievements page, an additional (optional) parameter called year has to be passed. This year corresponds to the Year of Achievement. According to the next line of code, if this parameter is null, then by default, the first year from the selected years will be passed into the parameter. Then those achievements whose Year of Achievement is identical to the year parameter are selected. After that, the Competition Types are distinctively selected to be used in the View. Then, the selected achievements are finally passed into the View.
<div class="achievements"> <% if (ViewData["Years"] != null) { %> <table style="width: 950px" cellspacing="10px"> <tr> <td class="list" style="width: 110px"> <div> <ul> <% foreach (var y in ViewData["Years"] as IEnumerable<int>) {%> <li> <h3> <%: Html.ActionLink(y.ToString(), "Achievements", new { year = y.ToString() })%></h3> </li> <%} %> </ul> </div> </td> <% if (ViewData["Types"] != null) { %> <td class="list"> <div class="verticalline"> <ul> <% foreach (var t in ViewData["Types"] as IEnumerable<string>) { %> <li> <h3> <%: t%></h3> <ul> <% foreach (var item in Model) {%> <% if (item.CompetitionType.TypeName == t) {%> <li> <p> <%: item.AchievementName%> <%: item.CompetitionName%></p> </li> <%} %> <%} %> </ul> </li>

<%} %> </ul> </div> </td> <%} %> </tr> </table> <%} %> </div>

This is part of the HTML and C# code used in the View of the Achievements page. Here, a table of one row and two columns have been created. The first row contains a list of the years as selected in the Controller. The second row contains the achievements categorized according to their Competition Type.

View Staff and Articles Actions This is the code used in the HomeController to display the Staff page. Here as well there is an additional parameter called cat (short for category). It refers to the Staff Category that the staff member belongs to. If the parameter cat is null it is set to the first or default category name in the table. Then the StaffCategories table is selected to be used and manipulated in the View. After that the parameter cat is cast into a string and stored in the string Category, again to be used in the View. A LINQ query is then used to select the staff member(s) whose Staff Category matches the parameter cat. Similar code is used for the Articles action as well.
public ActionResult Staff(string cat) { var context = new ISDataClassesDataContext(); if (string.IsNullOrEmpty(cat)) cat = context.StaffCategories.FirstOrDefault().CategoryName; ViewData["Categories"] = context.StaffCategories; ViewData["Category"] = cat.ToString(); var staff = from s in context.Staff where s.StaffCategory.CategoryName == cat select s; return View(staff); }

ControlPanel Controller This Controller is be used for the Actions to display pages of the Content Management Interface section. It is used also to manage Actions like creating, deleting or editing records stored in the database. These Actions can be performed only by users who are logged in. The C #codes used for these Actions for all the tables are almost completely the same. The sample codes that Ill be in including here is the code for creating, editing, deleting and displaying Staff records.

Retrieving Staff (CMI) Page


public ActionResult Staff() { var context = new ISDataClassesDataContext(); return View(context.Staff.ToList()); }

This code is used to list and display all the staff records.

Staff Form
public PartialViewResult StaffForm() { return PartialView(); }

This code is used to display the Staff form, which is a Partial View. I used a Partial View for the form to avoid using two separate ones for creating and editing staff members.
<div class="editor-label"> <%: Html.LabelFor(model => model.StaffName) %> </div> <div class="editor-field"> <%: Html.TextBoxFor(model => model.StaffName) %> <%: Html.ValidationMessageFor(model => model.StaffName) %> </div> <div class="editor-label"> <%: Html.LabelFor(model => model.PictureFilePath) %> </div> <div class="editor-field">

<input type="file" name="PictureFilePath"/> <%: Html.ValidationMessageFor(model => model.PictureFilePath) %> </div>

This is part of the Staff Form which shows code used to generate the text boxes and the file upload control. All the other forms are also similar to this.

ViewModels For the Create and Edit methods, an extra Model which I called the StaffViewModel is used. ViewModels are used to make validation of forms easier. The following is a part of the StaffViewModel.
namespace IrushaadhiyyaSchoolWebsite.ViewModels { public class StaffViewModel { [Required(ErrorMessage = "Please type in the name.")] [DisplayName("Name")] [StringLength(25, ErrorMessage="Name should not be longer than 25 characters")] public string StaffName { get; set; } [Required(ErrorMessage = "Please type in the job title.")] [DisplayName("Job Title")] public string JobTitle { get; set; } [DisplayName("Contact Number")] [StringLength(7)] [RegularExpression(@"\A[0-9]{7}\z", ErrorMessage="The contact number is invalid, it should be in the format nnnnnnn where n is a number.")] public string ContactNo { get; set; } [DisplayName("Email")] [RegularExpression(@"\A[a-zA-Z0-9\.\-_]+@{1}[A-Za-z0-9\.\-_]+(\.[a-zAZ]{2,3}){1,2}\z", ErrorMessage="The email address is invalid.")] public string Email { get; set; } [DisplayName("Picture (Optional)")] public HttpPostedFileBase PictureFilePath { get; set; } [Required(ErrorMessage = "Please select a category.")] [DisplayName("Staff Category")] public int? SelectedStaffCategory { get; set; } public IEnumerable<SelectListItem> StaffCategories { get; set; } } }

This is the StaffViewModel class with properties which are the same as the fields in the Staff table. Various Validation Attributes have been used for each property. Using the DisplayName attribute, I can change the name of the property that appears on the form. For the properties Contact Number and Email Address, custom made Regular Expressions have been used to validate the input. Regexes are used to make sure that the string entered is valid or not. It specifies the characters which can be used and which cannot. There are similar ViewModels for each editable table.

GET Method: Creating a new Staff Record


public ActionResult CreateStaff() { var context = new ISDataClassesDataContext(); var model = new StaffViewModel(); model.StaffCategories = from s in context.StaffCategories select new SelectListItem { Text = s.CategoryName, Value = s.StaffCategoryID.ToString() }; return View(model); }

This is the GET method for creating a staff member. Here, the StaffViewModel along with the ISDataClassesDataContext has been used. A new instance of the StaffViewModel is created and passed into the View. The 5th line of code is used to populate the drop-down list in the Staff form.

POST Method: Creating a new Staff Record


[HttpPost] public ActionResult CreateStaff(StaffViewModel model) { try { var context = new ISDataClassesDataContext(); if (ModelState.IsValid) { var newStaff = new Staff(); if (model.PictureFilePath != null) { string PictureFilePath = "~/Content/SiteImages/Teachers/" + model.PictureFilePath.FileName; model.PictureFilePath.SaveAs(Server.MapPath(PictureFilePath)); newStaff.PictureFilePath = PictureFilePath;

} newStaff.ContactNo = model.ContactNo; newStaff.Email = model.Email; newStaff.JobTitle = model.JobTitle; newStaff.StaffName = model.StaffName; newStaff.StaffCategoryID = model.SelectedStaffCategory.Value; context.Staff.InsertOnSubmit(newStaff); context.SubmitChanges(); return RedirectToAction("Staff", "ControlPanel"); } model.StaffCategories = from s in context.StaffCategories select new SelectListItem { Text = s.CategoryName, Value = s.StaffCategoryID.ToString() }; return View(model); } catch { return View(); } }

This is the code written for the POST method of creating a new staff record. First of all, a trycatch statement is used to handle exceptions. After creating a new instance of the DataContext, the rest of the code is executed if the Model state is valid (no form errors). A new instance of the StaffModelView is created and then the Http HttpPostedFileBase (files uploaded by the user) PictureFilePath is checked to see if it is empty or null. If it isnt null, then a string called PictureFilePath is created and the file path and root path of where the picture is to be saved to is stored in the string. This file path of the uploaded image is then stored in the Staff Model (newStaff). If the PictureFilePath of the Model is not null, then the above steps do not take place. The data in the StaffViewModel (model) is then stored in the Staff Model (newStaff). Then the data is inserted into the Staff table and a new record is created. If the record has been successfully created, the user is directed to the Staff page in the CMI section which shows a list of the table records. If the Model State is invalid, then a new record isnt created, instead the drop-down list is populated to avoid any errors, and the Model is returned. The StaffViewModel (model) will contain the details that the user had entered before form submission. If there is an exception, it is handled by the try-catch statement and View is returned. Almost the same code is used in creating new records in all other tables using forms, as well.

GET Method: Editing a Staff Record


public ActionResult EditStaff(int id) { var context = new ISDataClassesDataContext(); var model = new StaffViewModel(); model.StaffCategories = from s in context.StaffCategories select new SelectListItem { Text = s.CategoryName, Value = s.StaffCategoryID.ToString() }; var staff = (from s in context.Staff where s.StaffID == id select s).SingleOrDefault(); model.ContactNo = staff.ContactNo; model.Email = staff.Email; model.JobTitle = staff.JobTitle; model.StaffName = staff.StaffName; model.SelectedStaffCategory = staff.StaffCategoryID; return View(model); }

This is the GET method of the EditStaff action which has a parameter called id, which will be identical to the ID number of the Staff record to be edited. Using a LINQ query, the Staff record which has and ID number that is the same as the id parameter is selected. This record is passed into the StaffViewModel and displayed in the form for the user to see and edit.

POST Method: Editing a Staff Record


[HttpPost] public ActionResult EditStaff(int id, StaffViewModel model) { try { var context = new ISDataClassesDataContext(); if (ModelState.IsValid) { var staff = (from s in context.Staff where s.StaffID == id select s).SingleOrDefault(); string PictureFilePath = staff.PictureFilePath; if (model.PictureFilePath != null) { PictureFilePath = "~/Content/SiteImages/Teachers/" + model.PictureFilePath.FileName; model.PictureFilePath.SaveAs(Server.MapPath(PictureFilePath)); } staff.StaffName = model.StaffName;

staff.JobTitle = model.JobTitle; staff.ContactNo = model.ContactNo; staff.Email = model.Email; staff.StaffCategoryID = model.SelectedStaffCategory.Value; staff.PictureFilePath = PictureFilePath; context.SubmitChanges(); return RedirectToAction("Staff", "ControlPanel"); } model.StaffCategories = from s in context.StaffCategories select new SelectListItem { Text = s.CategoryName, Value = s.StaffCategoryID.ToString() }; return View(model); } catch { return View(); } }

Almost the same thing happens here in the POST method for the EditStaff Action. The difference is that the data in the StaffViewModel (model) is stored in the selected Staff record. If the whole process is successful, the user is directed to the Staff page in the CMI section. If the Model State is invalid, the StaffViewModel (model) is returned, and if there is an internal error in the application, the Edit page is returned to the user.

For the delete Action, code in the Controller is not enough. Ajax will be code will be written directly in the View.

Deleting a Staff Record


[HttpDelete] public ActionResult DeleteStaff(int id) { { try { var context = new ISDataClassesDataContext(); var staff = (from s in context.Staff where s.StaffID == id select s).SingleOrDefault(); context.Staff.DeleteOnSubmit(staff); context.SubmitChanges(); return RedirectToAction("Staff", "ControlPanel"); } catch { return View(); }

} }

This is the code in the Controller which simply selects the specified record according to its ID number and deletes it.
<%: Ajax.ActionLink("Delete", "DeleteStaff", "ControlPanel", new { id = item.StaffID }, new AjaxOptions { HttpMethod = "DELETE", Confirm = "Are you sure?", OnComplete = "refresh" })%>

This is the Ajax code used to direct the user to the delete action in the Controller after a confirmation message which appears when clicked on the link. The page will then be automatically refreshed showing an updated Staff page.

Common codes for all pages


<% if (Model.Count() == 0) {%> <br /> <br /> <br /> <br /> <br /> <div class="error"> <br /> <br /> <h2> No achievements have been added yet. </h2> <br /> <br /> </div> <br /> <br /> <br /> <br /> <br /> <%} %> <% else {%>

This is a snippet of code from the Achievements View page of the HomeController. This code checks whether there is any data in the Model, if there isnt any, that is, if the Model Count is 0, the user will be shown a message saying No achievements have been added yet. Else, if there is data in the Model, then the data is displayed as specified in the else statement.

This code snippet is repeated in all the pages except EventDetails and ArticleDetails. (The message shown may be a little different.)

Custom-made Html Helpers


public static class HtmlHelpers { public static string Truncate(this HtmlHelper helper, int number, string text) { if (text.Length > number) { return text.Substring(0, number) + " ..."; } return text; } }

This is a custom made Html Helper which truncates a given string (text) and shortens it to a given number of characters. If the texts length is greater than the integer number, then the text is truncated to that number of characters and returned with an additional string of ... at the end of the text.

Error Routing
protected void Application_Error() { var exception = Server.GetLastError(); var httpException = exception as HttpException; Response.Clear(); Server.ClearError(); var routeData = new RouteData(); routeData.Values["action"] = "Unknown"; routeData.Values["controller"] = "Error"; if (httpException != null) { Response.StatusCode = httpException.GetHttpCode(); switch (Response.StatusCode) { case 404: routeData.Values["action"] = "PageNotFound"; break;

default: routeData.Values["action"] = "Unknown"; break; } } Response.TrySkipIisCustomErrors = true; IController errorController = new ErrorController(); HttpContextWrapper wrapper = new HttpContextWrapper(Context); errorController.Execute(new RequestContext(wrapper, routeData)); }

This code has been used to route the user to an error page if one does occur. Using a switchcase statement, the error page to be displayed is decided. If it is a 404 error (page not found), then the Action to be referred to will be the PageNotFound Action. Otherwise, if any other error occurs, then the Unknown Action will be called. Miscellaneous Default picture for Staff Member records
<% if (!string.IsNullOrWhiteSpace(item.PictureFilePath)) {%> <img src="<%: Url.Content(item.PictureFilePath) %>" width="100px" height="100px" alt="<%: item.StaffName %>" /> <%} else {%> <img src="<%: Url.Content("~/content/siteimages/default.png") %>" width="100px" height="100px" alt="<%: item.StaffName %>" /> <%} %>

Each staff member has a picture, and the user (admin) has the choice to either upload an image or not. If the user has not uploaded any images, then the PictureFilePath will be empty. If it were to be displayed, but kept empty then an error would result. To solve this, using an if statement, the picture stored in the PictureFilePath would be displayed only if it wasnt empty. Otherwise, if it indeed was empty, then a default picture called default.jpg which is stored in the SiteImages folder will be set for that staff member.

Displaying the fifth menu block for logged in users


<div class="menublock"> <% if (Request.IsAuthenticated) {%> <%= Html.ActionLink("Content Management Interface", "CMI", "ControlPanel") %><br /> <hr noshade="noshade" /> <%= Html.ActionLink("Competition Types", "CompetitionTypes", "ControlPanel") %><br /> <%= Html.ActionLink("Achievements", "Achievements", "ControlPanel") %><br /> <%= Html.ActionLink("News & Announcements", "Articles", "ControlPanel") %><br /> <%= Html.ActionLink("Upcoming Events", "Events", "ControlPanel") %><br /> <%= Html.ActionLink("Staff", "Staff", "ControlPanel") %><br /> <%= Html.ActionLink("Static Pages", "StaticPages", "ControlPanel") %><br /> <%} %> </div>

This is a code snippet from the Master Page which first checks if the user is logged in or not. In other words, it checks whether the request to display the fifth menu block is authenticated or not. If the request is indeed authenticated, then the fifth menu block containing the Content Management Interface options is displayed. Otherwise, it is not.

Вам также может понравиться