Chapter 2

Creating the ASP.NET MVC 4 Project

EXERCISES AND EXAMPLES

IN THIS CHAPTER

  • Modifying the _Layout.cshtml to get a nice “somewhat” unique look and feel
  • Adding the methods required to list the blogs from the XML RSS file on the Home Index.cshtml page using Razor
  • Adding the Html.ActionLinks (C# Fundamentals, .NET Fundamentals, Advanced C#, and so on), and corresponding models, views, and controllers
  • Creating a local test SQL Server database called Blogs
  • Implementing NHibernate into the ASP.NET MVC 4 application
  • Creating the BlogNavBar partial view and add to a web page
  • Creating the Archive List, custom MapRoute and ViewData containers
  • Migrating a blog entry from ASP.NET with feedback form and comment list

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the Wrox.com code downloads for this chapter at www.wrox.com/go/azureaspmvcmigration on the Download Code tab. It is recommended that you download the code, review it so that you have a good understanding of what is about to be discussed. The steps in this chapter required to migrate the sample ASP.NET 2.0 website located at http://aspnet.thebestcsharpprogrammerintheworld.com to an ASP.NET MVC 4 application are provided in detail.

The initial approach for this chapter was to make the ASP.NET MVC 4 application have the same look and feel as the ASP.NET 2.0 website. But after spending some hours doing this, I made the decision to use the default template because: for one, it is different, but mostly because the whole intent of this book and the concept around it is to innovate, change, and improve what already exists. I am not a fan of change for the sake of change, but where you can change and something good comes from it, I am all for it. Therefore, the new blog website will have the look and feel of the ASP.NET MVC 4 template.


NOTE If you are not familiar with the concepts behind this chapter, please read through Chapter 1 before continuing.
Also, it is highly recommended that you download the ASP.NET and ASP.NET MVC 4 source code and review the code as you simultaneously walk through these instructions. Every step required to make the transition from ASP.NET to ASP.NET MVC 4 are not included.

CHANGING THE LOOK AND FEEL OF YOUR WEBSITE

As you know, building a website to meet the look and feel of your product or your client can be time-consuming. Fortunately, ASP.NET MVC has default projects, so you can save time and headaches by starting with a default project and changing the look and feel to suit your needs. Plus, keeping your website current with changes in style and customer tastes makes it much more appealing and keeps customers and visitors returning.

For this section, you start with an APS.NET MVC 4 project as illustrated in Figure 2-1. You then make changes to the look and feel of the website. For example, you might changes the font size and type of colors. The changes you make are performed primarily to two files: the _Layout.cshtml located in the ViewsShared folder and the Site.css located in the Content folder. These two files contain a majority of the styling attributes used globally across the website.

At the same time, you would change the content of this page (ViewsShared\_Layout.cshtml) to make it similar to what currently exists on the sample ASP.NET website.


NOTE The MVC source code for this chapter contains the completed website. To begin this exercise, create an ASP.NET MVC 4 (Internet Application) project within Visual Studio.

To make these changes, start at the top and work your way down by first opening the ViewsShared\_Layout.cshtml file. After all the changes, have been implemented the website will resemble the page that appears in Figure 2-2. These changes are described in the following numbered steps:

1. Change the content after the @ViewBag.Title to be the title of the website.
2. Replace the default favicon.ico file in the root folder to the one for this website.
3. Modify the Your Logo Here text to match the title of the website.
4. Open the ViewsShared\_LoginPartial.cshtml and modify the existing linkText.
5. Open the ViewsHomeIndex.cshtml file and make the following modifications:
  • Change the ViewBag.Title to C#. You can find the ViewBag.Message in the ControllersHomeController.cs file.
  • Modify the content of the page as required.
6. If necessary, change the color of the heroAccent.png file to match the color of the website.
7. Modify the contents of the footer.

Figure 2-2 shows the results of these steps which is the completed homepage.

You’ll spend most of your time in the ContentSite.css file. It is recommended that you do not make the look and feel changes directly to a view. Instead make all the styling happen in the CSS file and then reference the CSS class from within the view.

For example, to change the default blue color to gray, you would change the class references in the ViewsHomeIndex.cshtml file. The referenced class is .featured .content-wrapper. Open the ContentSite.css file and search for what is illustrated in Listing 2-1. When the file is opened, enter CTRL+F and enter .content-wrapper to quickly find the section of code.

LISTING 2-1: Example of Site.css File Featured Class

/* page elements
----------------------------------------------------------*/
/* featured */
.featured {
    background-color: #fff;
}
 
    .featured .content-wrapper {
        background-color: #7ac0da;
        background-image: -ms-linear-gradient(left, #7ac0da 0%, #a4d4e6 100%);
        background-image: -o-linear-gradient(left, #7ac0da 0%, #a4d4e6 100%);
        background-image: -webkit-gradient(linear, left top, right top, 
                           color-stop(0, #7ac0da), color-stop(1, #a4d4e6));
        background-image: -webkit-linear-gradient(left, #7ac0da 0%, #a4d4e6 100%);
        background-image: linear-gradient(left, #7ac0da 0%, #a4d4e6 100%);
        color: #3e5667;
        padding: 20px 40px 30px 40px;
    }
 
        .featured hgroup.title h1, .featured hgroup.title h2 {
            color: #fff;
        }
 
        .featured p {
            font-size: 1.1em;
        }

You would change the #7ac0da and #a4d4e6 to the color you want as the theme for the website. In this example the website is black and white therefore you use #000000 and #FFFFFF in all places where website colors are referenced.


NOTE To preserve the transparency of your images, use a program such as Paint.NET, such as the heroAccent.png image. If you make these changes using Paint, the background will likely be white.

The migration of the look and feel from the ASP.NET website and ASP.NET MVC 4 project are not covered in completed detail. Be sure that you are happy with the layout of the default website look and feel before continuing to the next section. The reason that this section does not give the steps for changing the look and feel in complete detail is so that you can set things up as you desire. Changing the outward appearance of a website does not require coding, it requires only HTML and CSS layout design and therefore the steps are not provided in detail. Instead, a sample converted website is provided for reference at http://mvc-4.cloudapp.net.

CREATING THE BLOG LIST FROM AN XML RSS FILE

The main page of the website contains a list of the most recently-posted blogs. The blogs are contained in an RSS XML file and are extracted using a LINQ to an XML query, as shown in Listing 2-2. The query results are used later in the exercises in order to list the most current blogs on the ASP.NET MVC 4 homepage.

LISTING 2-2: LINQ to XML Query

public static IEnumerable<BlogList> GetLinks(XDocument doc)
{
IEnumerable<BlogList> list = 
    (from item in doc.Elements("rss")
                     .Elements("channel")
                     .Elements("item")
            select new BlogList
            {
             Title = item.Element("title").Value,
              Url = item.Element("link").Value,
              Description = item.Element("description").Value
            }).Take(6);
        return list;
}

In the sample ASP.NET website, the XML file loads into the BlogList objects and binds to a GridView control embedded into the Default.aspx file. Although no one-to-one mapping exists between ASP.NET Gridview and ASP.NET MVC, you have several possibilities to cross this gap. Some are built in and some are third party. Options include:

  • JqGrid, which is part of the jQuery library
  • Telerik, which has some controls
  • MvcContrib, a download from CodePlex
  • The built-in WebGrid
  • Razor

All examples in this chapter use Razor capabilities, therefore when you create the views, remember to select Razor (CSHTML) from the drop-down and not ASPX (C#). The following are the basic steps required to list the most recent blogs in the XML RSS file:

1. Add a public class called BlogList to the Models folder.
2. Add two methods to the ControllersHomeControllers.cs file:
  • BlogListXML()
  • GetLinks(XDocument doc)
3. Modify the Index() method in the ControllersHomeControllers.cs file to return the list of blogs.
4. Modify the ViewsHomeIndex.cshtml file to display the list of blogs to the view.

The following sections cover all these steps in greater detail.

Adding the BlogList Class to the Models Folder

To add the BlogList model, you right-click the Models folder and then select Add ⇒ Class. Figure 2-3 illustrates the contents of the Add New Item window. You then name the class BlogList.cs and then click the Add button. Create the class as shown in Listing 2-3.

The BlogList class and any, for that matter, class is a template that defines the form of an object. When the BlogList class is instantiated and data loaded into it, that data can be accessed for manipulation and presentation. The BlogList class contains the data presented in the list on the homepage.

LISTING 2-3: The BlogList Class

public class BlogList
{
   public string Title { get; set; }
   public string Url { get; set; }
   public string Description { get; set; }
}

Adding Methods to the HomeControllers File

To add the BlogListXML() and GetLinks(XDocument doc) methods, you simply open the ControllersHomeControllers.cs file and add the them as shown in Listings 2-4 and 2-5. If you take a closer look at Listings 2-5 and 2-2, you will notice they are the same code. In this case, you can reuse the code you used with the ASP.NET 2.0 application in the ASP.NET MVC 4 without any modification.

The BlogListXML() method loads the XML file that contains the blogs, calls the GetLinks() method that executes the LINQ to XML query, populates the List<BlogList>, and returns the list.

LISTING 2-4: The BlogListXML() Method

using MVC.Models;
using System.Xml.Linq;
 
public List<BlogList> BlogListXML()
{
  XDocument doc = XDocument.Load(@"C:...MVCMVCContentRSScsharp2011.xml");
  IEnumerable<BlogList> list = GetLinks(doc);
 
  List<BlogList> resultList = new List<BlogList>();
  foreach (BlogList blog in list)
  {
    blog.Description = blog.Description.Substring(0, 120) + "...";
    resultList.Add(blog);
  }
  return resultList;
}

NOTE The code in Listing 2-4 might fail if the file does not exist. In an example in Chapter 6 this actually occurs The previous code is changed to use a C# method for loading files using a relative path.

The GetLinks() method (Listing 2-5) creates an IEnumerable list containing the BlogList class. The LINQ query restricts the result to six blogs as you can notice from the .Take(6) command. You want to do this to make the main page format look good. Leaving this command off results in the retrieval of all the blogs in the XML document, which fills up the default page.

LISTING 2-5: The GetLinks(XDocument doc) Method

public static IEnumerable<BlogList> GetLinks(XDocument doc)
{
  IEnumerable<BlogList> list = (from item in doc.Elements("rss")
                                                .Elements("channel")
                                                .Elements("item")
                                select new BlogList
                                {
                                  Title = item.Element("title").Value,
                                  Url = item.Element("link").Value,
                                  Description = item.Element("description").Value
                                }).Take(6); 
  return list;
}

Listing 2-6 is an example of the XML RSS file. Try and connect the .Elements selected in Listing 2-5 with the elements shown in the XML file, for example, rss, channel, item, title, link, and description.

LISTING 2-6: Example XML RSS File

<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <item>
      <title>Using the as keyword versus boxing in C#</title>
      <description>
        When I convert one object to another using the keyword "as" and the
        original value is not of that type, the converted value simply becomes 
        NULL.  For example, if theItem is of type MessageBox, then row will be
      </description>
      <link>
        http://www.thebestcsharpprogrammerintheworld.com/blogs/...boxing.aspx
     </link>
    </item>
  </channel>
</rss>

Modifying the Index() Method

Before you make modifications to the view (that is, ViewHomeIndex.cshtml), you must send the model from the controller. This is accomplished by adding a single line of code to the Index() method of the ControllerHomeController.cs and modifying the returned value. Listing 2-7 provides an example of how this method should look.

LISTING 2-7: Index() of the HomeController.cs

public ActionResult Index()
{
  ViewBag.Message = "This site is created to show ... Windows Azure";
  var blogs = BlogListXML();
  return View(blogs);
}

NOTE A ViewBag is special to MVC and is a public dynamic property declared in the ControllerBase class, which implements the IController interface. When you declare something as dynamic, this means that the actual type is only known at run time and that you do not need to be concerned with the value stored in the ViewBag property. To view the definition, right-click ViewBag and then click Go To Definition.

Displaying the List of Blogs in the View

Now that the contents of the XML file are loaded into a List<BlogList> object and is sent back to the view, it’s time to bind that list to the view.


NOTE If multiple models need to be returned to a view, use the ViewData[] collection that supports this requirement. This gets or sets a dictionary containing the data passed between a controller and the view.

To do this, open the ViewsHomeIndex.cshtml file and add the following line of code as the first line:

@model List<MVC.Models.BlogList>

The loading and binding worked without that line of code because only a single model is returned. However the IntelliSense that you used later in the foreach loop was not enabled without it. So, you should add it because it makes things easier. Finally, add the HTML/Razor code to the Index.cshtml file, as shown in Listing 2-8, to display the list of blogs.

LISTING 2-8: Markup and Razor Code to Display the XML RSS Results

<table>
@foreach (var item in Model)
{
  <tr>
    <th>
      @item.Title
    </th>
  </tr>
  <tr>
    <td>
      @item.Description
    </td>
  </tr>
  <tr>
    <td>
      <a href="@item.Url">More...>>></a>
    </td>
  </tr>
}
</table>

When you press F5 to run the ASP.NET MVC application, you see the website rendered (refer to Figure 2-2).

ADDING THE HTML.ACTIONLINKS

You should have transformed the main page to how it needs to look (refer to Figure 2-2) and the page should contain the required functionality. If not, make the necessary changes based on your tastes, or copy the code from the downloadable ASP.NET MVC 4 example. The link to the source code is provided at the beginning of the chapter. The next step is to add links to the pages that take you to the default website page. For many of the example pages, a Model is not needed — only the controller and view.

Before the links are added to the default page (that is, the ViewHomeIndex.cshtml view) you must first create a page to link to. In this section, you create the CSharpFundamentals view and link to it from the ViewHomeIndex.cshtml view. To do so, follow these steps:

1. Add a controller to the MVC application by right-clicking the Controllers folder and then clicking Add ⇒ Controller.
2. In the Add Controller window, add the contents shown in Figure 2-4, and click the Add button. In this figure, the Empty MVC Controller option is selected because the page you’re linking to does not require any data retrieval or modification. If this example had used, for example ADO.NET Entity Framework, you could select the specific model and readwrite can be dynamically generated. The CSharpFundamentalsController requires no modification because the page is basically static.
3. To add a view, right-click the Index() method of the CSharpFundamentalsController.cs file, and select Add Views, as shown in Figure 2-5.
4. In the Add view window, leave the default values as shown in Figure 2-6, and click the Add button. A folder called CSharpFundamentals is created in the Views folder automatically. It contains a file called Index.cshtml. This is the file where you place the static content to present to the visitor of the page.
5. Add the link from the main page (HomeIndex.cshtml) to the Index.cshtml in the CSharpFundamentals view. You do this by adding the following code snippet to the HomeIndex.cshtml file.
@Html.ActionLink("C# Fundamentals", "Index", "CSharpFundamentals")

NOTE In the previous code snippet, the "C# Fundamentals" value is the text that appears on the HomeIndex.cshtml, and it leads to the CSharpFundamentalsIndex.cshtml page (that is, linkText). The "Index" is the actionName that matches the method found in the associated C# controller (that is, Index()).

6. Press F5 to run the ASP.NET MVC 4 application.
7. Clicking the C# Fundamentals link renders the CSharpFundamentals/Index.cshtml view.

Recall that when the CSharpFundamentals view is created the defaults are left unchanged. Due to this, you use the Views\_ViewStart.cshtml layout to render the CSharpFundamentals View. This feature is identical to the Master Pages concept in the ASP.NET platform. By having a default layout, you maintain the look and feel across the entire website. This makes it much easier to implement a look and feel change if required. For example, if you must change the size or color of the text in the <table> tag, making a change to the CSS class in the Site.css file impacts the entire website. Likewise, if you want to change the content shown from the _ViewStart.cshtml, you need to make the change only once. This is much better than having to access each web page and make a specific change to each.

Up to now the Home and C# Fundamentals pages have been migrated from ASP.NET 2.0 to ASP.NET MVC 4. This example website has two more pages containing only static text — the Blogs and Lessons page. You can use the process for migrating the C# Fundamentals and Home pages to migrate these other two static pages using the following actions:

  • Add a Controller.
  • Add a View.
  • Add the Html.ActionLink to the HomeIndex.cshtml.

The Html.ActionLink() methods implemented so far are presented in the following snippet. As you can see, there are links on the HomeIndex.cshtml page for Home, Blogs, Lessons, and C# Fundementals, which all call the Index() method of their respective Controller. The name of the Controller is the last parameter of the Html.ActionLink() method parameter list.

@Html.ActionLink("Home", "Index", "Home")
@Html.ActionLink("Blogs", "Index", "Blogs")
@Html.ActionLink("Lessons", "Index", "Lessons")
@Html.ActionLink("C# Fundamentals", "Index", "CSharpFundamentals")

The remaining pages you’re migrating to ASP.NET MVC 4 either contain a partial view that makes a database query or the page itself contains a database create, read, update, or delete (CRUD) action. Therefore, the next section describes how to implement NHibernate into the ASP.NET MVC 4 application.

CREATING A LOCAL TEST DATABASE

The database part of this migration uses the Model-First design concept. This means that you must create the relationships between the classes as well as creating the classes themselves before creating the database tables and their foreign key relationships. NHibernate, like the Entity Framework, provides the capabilities for this. However, both NHibernate and Entity Framework require you first create a database in which to create the tables. In other words, the Object Relational Mappings (ORMs), such as NHibernate or Entity Framework, can create the tables and relationships between the tables for you, but they won’t create the database itself.


NOTE For more information on ORM’s, see Chapter 1.

Later chapters provide details for creating and migrating databases to the Windows Azure platform. In this example, however, because development happens locally on a PC, you create the database instance using Microsoft SQL Server 2012 Express. To create this local database, follow these basic steps, which are outlined in detail in the following sections:

1. Download and install Microsoft SQL Server 2012 Express (for example, SQLEXPR_x86_ENU.exe).
2. Create a new SQL Server Database using Visual Studio 2012.

Downloading and Installing SQL Server

To download and install SQL Server, you can do a search using any search engine for SQL Server 2012 Express. This likely renders the link to the Microsoft download site. There are many different versions of the download. You should choose the one appropriate to your platform and install the database on your computer.


NOTE Although this example uses Microsoft SQL Server, you can use Oracle, MySQL, and so on by changing the drivers and connection strings.

The installation process prompts the user with many options. To complete the installation process, follow these steps:

1. Choose the New SQL Server standalone option shown in Figure 2-7 to install the database. The Database Engine Configuration dialog appears (Figure 2-8).
2. Accept all the defaults except that shown in Figure 2-8. The plan is to use SQL Server authentication to control the access to the database. The other option is to allow Windows Authentication only, which is not what is used in this example. Be sure to write down the password for later use.

Creating a New SQL Server Database

To create the database, first, you need to open the Server Explorer in Visual Studio and then follow these steps:

1. Right-click Data Connections and then select Create New SQL Server Database, as shown in Figure 2-9. This opens the Create New SQL Server Database (Figure 2-10).
2. Enter the values shown in Figure 2-10. The values include the Server Name, which it the name of your PC, because the database is being created locally followed by SQLExpress. For this example, you use SQL Server Authentication and create a username and password. The username and password can be anything you choose. Just remember them for later use. Lastly, enter the name of the database, in this case Blog.

NOTE If you right-click the database you just created and select Properties (bottom right of the Visual Studio IDE), you can find the value of the Connection String property. This is useful as a guideline format to create the database Connection String in the application.

The database instance named Blogs is now created and ready for use. In the following section, you can find the instructions required to configure NHibernate, design the model, and create the tables are discussed in detail.

IMPLEMENTING NHIBERNATE INTO AN ASP.NET MVC 4 APPLICATION

This section illustrates how to perform an implementation using C# code. The actions required to implement NHibernate into an ASP.NET MVC 4 application follows:

  • Download, install, and add NHibernate references.
  • Create the class and configuration mappings.
  • Add the NHibernate configuration settings to the Global.asac.cs file. Download, and install NHibernate.

NOTE My previous book, Working with NHibernate 3.0, has a chapter that covers implementing NHibernate into an ASP.NET MVC 3 application.

You can find the most current version of NHibernate on the official website: www.nhforge.org or install it from the package manager in Visual Studio 2012. For this migration, version 3.3.1 is used.

The installation of NHibernate is relatively simple and can be completed in a number of ways:

  • Copy the two dependent DLLs shown in Figure 2-11 into a directory on your PC. Then, in Visual Studio with the ASP.NET MVC 4 application open, right-click the References folder in the Solution window, and then click Add Reference. Browse to the location where you stored the two DLLs and add them to the solution.
  • A faster option is to use Visual Studio 2012’s Library Package Manager. The Library Package Manager uses the NuGet package management system. With the ASP.NET MVC 4 application open, select Tools ⇒ Library Package Manager ⇒ Package Manager Console. This results in the Package Manager Console being rendered at the bottom of the Visual Studio IDE. Figure 2-12 illustrates this. You then enter install-package NHibernate, and the most current version of NHibernate installs into your project. In Figure 2-13, the same two DLLs as those shown in Figure 2-11 are installed into the References directory. In this case, the versions are actually newer than the ones downloaded from the NHibernate website.

NOTE NuGet is an open source tool that is helpful for integrating third-party libraries directly into a .NET application.

At this point, NHibernate is installed and ready for usage.

CREATING THE NHIBERNATE CLASSES AND CONFIGURATION

Now that NHibernate is installed, the remaining thing to do is add the using{} statement in the C# files to access its capabilities. To do this, you create a static class-level SessionFactory, a method that configures NHibernate called ConfigureNHibernate(), and the classes used in the ASP.NET MVC 4 application that need the database tables, Blog and Comments. Then implement the code that exports the schema, creates the database, validates the schema, and inserts the default data. To do all this, follow these steps:

1. Add a class-level SessionFactory to the Global.asax.cs file, as shown in Listing 2-9.

LISTING 2-9: Adding the SessionFactory to the Global.asax.cs File

using NHibernate;
namespace MVC
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static ISessionFactory SessionFactory { get; private set; }
 
        ...
    }
}
2. In the same file (Global.asax.cs) create a method called ConfigureNHibernate(), as shown in Listing 2-10. Remember that you can retrieve the value for the db.ConnectionString by right-clicking the Blogs database under the Database connections list.

LISTING 2-10: NHibernate configuration Method

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System.Data;
private static Configuration ConfigureNHibernate()
{
  var configure = new Configuration();
  configure.SessionFactoryName("MVC-TheBestCSharpProgrammerInTheWorld");
 
  configure.DataBaseIntegration(db =>
  {
    db.Dialect<MsSql2008Dialect>();
    db.Driver<SqlClientDriver>();
    db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
    db.IsolationLevel = IsolationLevel.ReadCommitted;
    db.ConnectionString = @"Data Source=BENW8SQLExpress;
                           Initial Catalog=Blogs;Persist
                           Security Info=True;User ID=sa;
                           Password=**;Pooling=False";
    db.Timeout = 10;
 
    // enabled for testing
    db.LogFormattedSql = true;
    db.LogSqlInConsole = true;
    db.AutoCommentSql = true;
  });
 
  return configure;
 
}
3. Call the ConfigureNHibernate() method from within the Application_Start() method of the Global.asax.cs file, as shown in Listing 2-11. Add the method call to the end of the existing Application_Start() logic. The session context has been set to WebSessionContext.

Listing: 2-11: Executing the ConfigureNHibernate() Method

using NHibernate.Context;
var configuration = ConfigureNHibernate();
configuration.CurrentSessionContext<WebSessionContext>();
4. Create the classes and the relationship between the classes. These classes are what NHibernate ultimately uses to create the tables in the database. Add a class called Blog to the Models directory by right-clicking the folder; then click Add ⇒ Class ⇒ Blog.cs ⇒ Add. The structure of the class is shown in Listing 2-12.

LISTING 2-12: The Blog Class

public class Blog
{
  public Blog() 
  {
    comments = new List<Comments>();
  }
 
  public virtual int Id { get; set; }
  public virtual string Title { get; set; }
  public virtual string Type { get; set; }
  public virtual string FileName { get; set; }
  public virtual DateTime CreationDate { get; set; }
  public virtual string Category1 { get; set; }
  public virtual string Category2 { get; set; }
  public virtual string Category3 { get; set; }
  public virtual string Category4 { get; set; }
  public virtual Decimal Rating { get; set; }
  public virtual int NumberOfRatings { get; set; }
  public virtual int Advanced { get; set; }
 
  public virtual IList<Comments> comments { get; set; }
}

NOTE At this point, you have a reference to the Comments class, which has not yet been created in this class. These Comments class references — for example in the Blog() constructor and as the type of elements in the IList — retrieve the comments for a specific blog. Just ignore the error until after the Comments class is created, as shown in Listing 2-13.

5. Add another class to the Models directory by right-clicking the Models directory and then clicking AddClass ⇒ Comments.cs ⇒ Add. Listing 2-13 shows the structure of the Comments class.

LISTING 2-13: The Comments Class

public class Comments
{
   public Comments() { }
 
   public virtual int Id { get; set; }
   public virtual string Subject { get; set; }
   public virtual string Comment { get; set; }
   public virtual string Email { get; set; }
   public virtual string Name { get; set; }
   public virtual string Password { get; set; }
   public virtual string Url { get; set; }
   public virtual DateTime TimeStamp { get; set; }
   public virtual string Type { get; set; }
 
   public virtual Blog blog { get; set; }
}

NOTE Before the release of NHibernate 3.0, the mappings of the classes to a database table generally took place using mapping files. These files are XML-based and required that the developer manage the structure of the class and its relationship with the database in two different places — one in the class file and one in the mapping file. With the release of NHibernate 3.0, there came the concept of mapping by code that eliminated the need for the mapping/xml files. Nonetheless, using XML files to map your classes to a database is still supported and widely used.

6. To map the Blog class, open the Blog.cs file and add the code in Listing 2-14. Include the NHibernate.Mapping.ByCode and NHibernate.Mapping.ByCode.Conformist namspaces — these provide the methods required to perform the mappings. Notice that the class contains a Bag. This custom container is the link between the Blog class and the Comments class. This relationship is what NHibernate uses to create the database relationship.

LISTING 2-14: The BlogMap Class

using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
public class BlogMap : ClassMapping<Blog>
{
  public BlogMap()
  {
    Id<int>(x => x.Id);
    Property<string>(x => x.Title);
    Property<string>(x => x.Type);
    Property<string>(x => x.FileName);
    Property<DateTime>(x => x.CreationDate);
    Property<string>(x => x.Category1);
    Property<string>(x => x.Category2);
    Property<string>(x => x.Category3);
    Property<string>(x => x.Category4);
    Property<Decimal>(x => x.Rating);
    Property<int>(x => x.NumberOfRatings);
    Property<int>(x => x.Advanced);
 
    Bag<Comments>(x => x.comments, cp => {}, 
                  cr => cr.OneToMany(x => x.Class(typeof(Comments))));
  }
}
7. As with the BlogMap class, add a CommentsMap class, as shown in Listing 2-15. Notice that instead of a custom Bag container, there is simply a ManyToOne relationship created of type Blog.

LISTING 2-15: The CommentsMap Class

public class CommentsMap : ClassMapping<Comments>
{
 public CommentsMap()
 {
   Id<int>(x => x.Id);
   Property<string>(x => x.Subject);
   Property<string>(x => x.Comment);
   Property<string>(x => x.Email);
   Property<string>(x => x.Name);
   Property<string>(x => x.Password);
   Property<string>(x => x.Url);
   Property<DateTime>(x => x.TimeStamp);
   Property<string>(x => x.Type);
   ManyToOne<Blog>(x => x.blog);
  }
}
8. Return to the Global.asax.cs file located in the root directory of the MVC solution, and add the method shown in Listing 2-16. The GetMappings() method loads the classes and compiles them so that they can be used to create the data structure.

LISTING 2-16: The GetMappings() Method

using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
protected static HbmMapping GetMappings()
{
 ModelMapper mapper = new ModelMapper();
 mapper.AddMapping<MVC.Models.BlogMap>();
 mapper.AddMapping<MVC.Models.CommentsMap>();
 return mapper.CompileMappingFor(new[] { typeof(MVC.Models.Blog), 
                                         typeof(MVC.Models.Comments) });
}
9. Add the lines of code shown in Listing 2-17 to the Application_Start() method of the Global.asax.cs file. Add this code below the code added in Listing 2-11.

LISTING 2-17: Mapping by Code – NHibernate Methods

using NHibernate.Tool.hbm2ddl;
HbmMapping mapping = GetMappings();
configuration.AddDeserializedMapping(mapping, "ASP.NET.MVC.4");
SchemaMetadataUpdater.QuoteTableAndColumns(configuration);
SessionFactory = configuration.BuildSessionFactory();
At this point, everything is set up and ready to go from an NHibernate perspective. However, the first time or anytime you want to create the database schema, you must take some additional steps. These methods must run only once to initially create the database schema using the classes created previously. Listing 2-18 should be executed only when the database schema needs to be created, dropped, and/or re-created.
10. Add the code shown in Listing 2-18 to the Global.asax.cs file after the code added in Listing 2-17. This code snippet calls the Drop() and Create() methods of the SchemaExport NHibernate class.

LISTING 2-18: NHibernate Create, Validate, and Insert Data into Database Schema

//Comment this code out unless you want to build 
//the database at the start of each application
try
{
  new SchemaExport(configuration).Drop(false, true);
  new SchemaExport(configuration).Create(false, true);
  SchemaValidator schemaValidator = new SchemaValidator(configuration);
  schemaValidator.Validate();
  //Insert some default data if required
  }
  catch (HibernateException e)
  {
    //Log the error in the Event Viewer using the System Diagnostics library
  }

WARNING Be certain that you want to Drop() your database before you run this method because the method deletes everything. The SchemaValidator ensures everything works all right, and that your database schema and the mappings are the same.

Figure 2-14 shows the status of the database before and after the running of the website.
11. Expand the Comments table so that the columns are visible, and notice the column named Blog. This is actually a foreign key that makes the connection between the Blog and the Comments. NHibernate also marks the ID columns as the primary key for both the Blog and the Comments table.

In the downloadable sample website mentioned at the beginning of this chapter you can find some example data into the tables. To access them, you can search for the InsertTestData() method located in the Global.asax.cs file.

This completes the implementation of NHibernate into an ASP.NET MVC application. The next two sections give examples of using NHibernate in the application to retrieve data.

CREATING AND ADDING THE BLOGNAVBAR PARTIAL VIEW

The BlogNavBar in this sample website is what contains the Translator, Blog Statistics (Counts and Archives), Disclaimer, Blog Archive, and so on. This navigation bar/partial view is part of many pages contained on the website. Every blog has this view added to the right side of the page. In addition, the .NET Fundamentals, Reviews, Archive List, Advanced C#, and so on pages have this partial view included. As discussed in Chapter 1, it makes sense to create a control or partial view and reuse it across the website instead of adding the code individually to each page.

The BlogNavBar contains two sections that require data retrieval:

  • Blog Statistics: Contains the number of blogs, number of comments and the number of fundamental blogs.
  • Blog Archive List: Contains a list of blogs by month and year along with the number of blogs for that month.

The following actions are required to complete the creation of the BlogNavBar:

1. Add the _BlogNavBar.cshtml partial view to the ViewsShared directory.
2. Add the static content to the view.
3. Create a sample view (netFundamentals.cshtml) and add the BlogNavBar partial view.
4. Add the logic to retrieve and present database content.

The sections further detail these general steps to get your partial view up and running.

Adding a Partial View

You can easily add the partial view to the _BlogNavBar.cshtml by following these steps:

1. Right-click the ViewsShared directory, and then select Add View. The Add View dialog appears.
2. Change the view name to BlogNavBar.
3. Select the Create as Partial View check box.
4. Click the Add button.

Figure 2-15 illustrates the values for the creation of this partial view.

Adding Static Content to the View

Again, use the ASP.NET sample website code contains the static content for this partial view, or create your own content and structure of the BlogNavBar.


NOTE The ASP.NET BlogNavBar control is found in the Include directory of the ASP.NET sample allocation, and the file is called BlogRightColumn.ascx. The sample ASP.NET website contained with the file named Chapter 2 - CSHARP.ASP.NET is downloadable from the Wrox website.

Creating a Sample View and Adding the _BlogNavBar

Now that you’ve added static content to the BlogNavBar partial view, you can add it to a web page in the test ASP.NET MVC 4 website. You use the netFundamentals.aspx file from the sample ASP.NET website as a base for this next task and the content for the page. Follow these steps to convert and place this file into the ASP.NET MVC 4 application:

1. Add a new View to the ViewsCSharpFundamentals directory called netFundamentals.cshtml. To do so, right-click the Views CSharpFundamentals directory, and select Add ⇒ View. Then change the view name to netFundamentals, leaving all other values as default. Finally, click the Add button. The netFundamentals.cshtml file is then added to the directory.
2. Modify the CSharpFundamentalsController.cs to add the required ActionResult. Add the method shown in Listing 2-19 to the ControllersCSharpFundamentalsController.cs file.

LISTING 2-19: The Default ActionResult Method for the netFundamentals.cshtml View

public ActionResult netFundamentals()
{
  return View();
}
3. Add the ActionLink to the ViewsShared\_Layout.cshtml to call the ActionResult method you added in step 2. Add the following code snippet to the ViewsShared\_Layout.cshtml directly under the C# Fundamentals Html.ActionLinks added in the previous section “Adding the Html.ActionLinks.”
@Html.ActionLink(".NET Fundamentals", "netFundamentals", "CSharpFundamentals")
4. Test that the link works and the netFundamental.cshtml file is opened. Press F5 in Visual Studio 2012, and, after the main page is rendered, click the .NET Fundamentals link on the top left of the web page, as shown in Figure 2-16. The default netFundamentals.cshtml page is rendered using the _Layout.cshtml view that keeps the look and feel of the website consistent.
5. Create the netFundamental.cshtml page. Use the netFundamentals.aspx page in the sample ASP.NET code.
6. Add the BlogNavBar partial view to the netFundamentals.cshtml. Migrate the content and look and feel to the new netFundamentals.cshtml page.
7. Add the dynamic content to the _BlogNavBar.cshtml partial view. Essentially, you add the ViewShared\_BlogNavBar.cshtml partial view to the ViewCSharpFundamentals etFundamentals.cshtml page. This is done by adding the following code snippet to the ViewCSharpFundamentals etFundamentals.cshtml view:
@Html.Partial("_BlogNavBar")
There really is nothing significant to discuss with the creation of the static content for this file. It is simple HTML, tables, row, columns, some bulleted lists, and some content.

NOTE The ASP.NET MVC 4 sample website can be downloaded from the Wrox website. If you need some tips or examples on how to create these pages, everything in these examples can be found there.

8. Run the ASP.NET MVC 4 project. Select F5 and then to click the .NET Fundamentals links, as shown in Figure 2-16. The ViewShared\_BlogNavBar.cshtml partial view renders along with the ViewShared\_Layout.cshtml and the ViewCSharpFundamentals etFundamentals.cshtml view.

Adding Dynamic Content to the _BlogNavBar Partial View

In this section, you add the dynamic content that is retrieved from the database. In this example, the count of the total number of blogs, the count of the total number of comments, the count of the total number of fundamental articles, and the list of blog archives by YEAR and MONTH are the dynamic variables that need retrieval from the database. The following sections outline each database variable in greater detail.

Adding the Blog Archive by Year Month Partial View

The objective of this section is to extract the Year, the Month, and the number of blogs written during that time frame (that is, Year and Month) from the blog database table. The query shown in Listing 2-20 provides the required results.

LISTING 2-20: Select Year, Month, and Count from the Blog Table

"SELECT DATENAME(YEAR, CREATIONDATE) AS YEAR, " +
"DATENAME(MONTH, CREATIONDATE) AS MONTH, COUNT(*) AS COUNT FROM Blog " +
"WHERE DATENAME(YEAR, CREATIONDATE) = DATENAME(YEAR, GetDate()) " +
"GROUP BY DATENAME(MONTH, CREATIONDATE), DATENAME(YEAR, CREATIONDATE) " +
"ORDER BY CASE LEFT(DATENAME(MONTH, CREATIONDATE), 3) " +
"WHEN 'JAN' THEN '01' " +
"WHEN 'FEB' THEN '02' " +
"WHEN 'MAR' THEN '03' " +
"WHEN 'APR' THEN '04' " +
"WHEN 'MAY' THEN '05' " +
"WHEN 'JUN' THEN '06' " +
"WHEN 'JUL' THEN '07' " +
"WHEN 'AUG' THEN '08' " +
"WHEN 'SEP' THEN '09' " +
"WHEN 'OCT' THEN '10' " +
"WHEN 'NOV' THEN '11' " +
"WHEN 'DEC' THEN '12' " +
"ELSE '' END DESC";

Two things to mention about this query:

  • It focuses only on the blogs written in the current year. Each year on this blog, the blogs from the previous year were hard-coded. This was done for scalability reasons. Over time, if the projection of a 1-year time window was not implemented, the amount of retrieved data and the query execution time would increase. Therefore, it’s a good idea to place this constraint on the query.
  • The result of this query is not strongly typed. The query returns the year, month, and count, and there is not an existing class that has this structure, nor is there a NHibernate mapping class to connect it to. This means that the result of the query is not directly bound to a class and is therefore loaded into the type object.

You can begin the retrieval of this blog archive list by following these steps:

1. Create a class called BlogNavBarElements, as shown in Listing 2-21.
2. Add the class to the Models directory of the ASP.NET MVC 4 solution by right-clicking the Models directory and then clicking Add ⇒ Class. Enter BlogNavBarElements in the Name text box and then click the Add button.

LISTING 2-21: The BlogNavBarElements Class

public class BlogNavBarElements
{
  public int blogCount { get; set; }
  public int commentsCount { get; set; }
  public int fundamentalsCount { get; set; }
  public List<BlogArchives> blogArchiveList { get; set; }
}
This class is a hybrid class, meaning that it contains all dynamic content for the BlogNavBar partial view including the List<BlogArchives> collection.
3. The BlogArchives class is shown in Listing 2-22. Add it to the Models directory of the ASP.NET MVC 4 solution by right-clicking the Models directory and then clicking Add ⇒ Class. Enter BlogArchives in the Name textbox and then click the Add button.

LISTING 2-22: The BlogArchives Class

public class BlogArchives
{
  public string Month { get; set; }
  public string Year { get; set; }
  public string Count { get; set; }
}
4. In the ModelsBlogNarBarElements class add a method called GetBlogNavBarContent(), as shown in Listing 2-23.
In this listing, the class type storing the return of blogsQuery.List<object> is of type IList<object> object. This is mentioned again to draw special attention that you cannot cast an untyped object directly to the ModelsBlogArchives class (as shown in Listing 2-22). In Listing 2-23, the IList<object> baList is cast to a List<string> and then added to the List<BlogArchives> collection of the ModelsBlogNavBarElements class.

NOTE As a test, try to convert BlogNavBarElements directly from object to BlogArchives to see what happens. See if you can come up with a different or better way to implement this.

LISTING 2-23: Get the Year, Month, and Count of the Blogs for the Current Year

using NHibernate;
 
public BlogNavBarElements GetBlogNavBarContent()
{
  BlogNavBarElements elements = new BlogNavBarElements();
  string Hql = "SELECT DATENAME(YEAR, CREATIONDATE) AS YEAR, " +
  "DATENAME(MONTH, CREATIONDATE) AS MONTH, COUNT(*) AS COUNT FROM Blog " +
  "WHERE DATENAME(YEAR, CREATIONDATE) = DATENAME(YEAR, GetDate()) " +
  "GROUP BY DATENAME(MONTH, CREATIONDATE), DATENAME(YEAR, CREATIONDATE) " +
  "ORDER BY CASE LEFT(DATENAME(MONTH, CREATIONDATE), 3) " +
  "WHEN 'JAN' THEN '01' " +
  "WHEN 'FEB' THEN '02' " +
  "WHEN 'MAR' THEN '03' " +
  "WHEN 'APR' THEN '04' " +
  "WHEN 'MAY' THEN '05' " +
  "WHEN 'JUN' THEN '06' " +
  "WHEN 'JUL' THEN '07' " +
  "WHEN 'AUG' THEN '08' " +
  "WHEN 'SEP' THEN '09' " +
  "WHEN 'OCT' THEN '10' " +
  "WHEN 'NOV' THEN '11' " +
  "WHEN 'DEC' THEN '12' " +
  "ELSE '' END DESC";
  using (ISession session = MvcApplication.SessionFactory.OpenSession())
  using (ITransaction transaction = session.BeginTransaction())
  {
    IQuery blogsQuery = session.CreateSQLQuery(Hql);
    IList<object> baList = blogsQuery.List<object>();
    elements.blogArchiveList = new List<BlogArchives>();
    foreach (object[] archive in baList)
    {
      List<string> fields = archive.Select(i => i.ToString()).ToList();
      elements.blogArchiveList.Add(new BlogArchives
      {
        Year = fields[0].ToString(),
        Month = fields[1].ToString(),
        Count = fields[2].ToString()
      });
    }
  }
  return elements;
}
5. Add the code to the netFundamentals() method of the ControllersCSharpFundamentalsController controller, as shown in Listing 2-24. This method creates an instance of the ModelsBlogNavBarElements class and calls the GetBlogNavBarContent() method. The result of the method is stored in a ViewData object named blognavbar, which is returned to the view for presentation.

LISTING 2-24: Return the Year, Month, and Count of the Blogs for the Current Year Using ViewData

BlogNavBarElements blogElements = new BlogNavBarElements();
ViewData["blognavbar"] = blogElements.GetBlogNavBarContent();
return View(ViewData);

Before making the necessary changes to the ViewsShared\_BlogNavBar.cshtml partial view, finish the GetBlogNavBarContent(). The next section discusses the final modifications to this method.

Adding Total blogs, Total Comments and Total Fundamentals

Retrieving and formatting the Blog Archives were a little challenging. The conversion from object to string to BlogArchive took some effort. If you have made it through this chapter, the remaining three code listings should not be a problem.

1. The first code segment, which you add to the GetBlogNavBarContent() method of the ModelsBlogNavBarElements class, is shown in Listing 2-25. It retrieves and stores the total number of blogs.

LISTING 2-25: Total Blog Count Query

using (ISession session = MvcApplication.SessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
  IQuery blogQuery = session.CreateQuery("Select count(*) from Blog");
  elements.blogCount = Convert.ToInt32(blogQuery.UniqueResult());
}
2. Add the following code segment shown in Listing 2-26. It retrieves and stores the total number of comments.

LISTING 2-26: Total Comments Count Query

using (ISession session = MvcApplication.SessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
  IQuery commentsQuery = session.CreateQuery("Select count(*) from Comments");
  elements.commentsCount = Convert.ToInt32(commentsQuery.UniqueResult());
}
3. Add the code shown in Listing 2-27 to the GetBlogNavBarContent() method of the ModelBlogNavBarElements class to retrieve and store the total count of articles of type 'fundamentals'.

LISTING 2-27: Total Fundamentals Count Query

using (ISession session = MvcApplication.SessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
  IQuery fundamentalsQuery = session.CreateQuery(
                                "Select count(*) 
                                 from Blog 
                                 where Type = 'fundamentals'");
  elements.fundamentalsCount = Convert.ToInt32(fundamentalsQuery.UniqueResult());
}

That’s it. Now that all the dynamic content has been retrieved from the database and has been passed to the view, it’s time to access it and display it within the view.

Displaying the Dynamic Data

The BlogNavBar partial view contains a number of Dynamic data that require database access and data retrieval. This section explains how to access the methods just written so that the information and data provided by them is presented to the website visitor.

1. Add the following code snippet to line 1 of the ViewsShared\_BlogNavBar.cshtml partial view:
@model MVC.Models.BlogNavBarElements
2. Add the following code snippets to the location where the total number of blogs, total number of comments and total number of fundamentals are displayed in the BlogNavBar. You add the code snippets to the ViewShared\_BlogNavBar.cshtml file.
Blogs:        @Html.ValueFor(b => b.blogCount)
Comments:     @Html.ActionLink(Model.commentsCount.ToString(), "Comments", "Blogs")
Fundamentals: @Html.ActionLink
              (Model.fundamentalsCount.ToString(), "Index", "CSharpFundamentals")

NOTE The Comments action ID has not been created at this point, and you will receive an error message if the project is run and the link is selected. You can find the view in the ViewsBlogs directory and the code handling the request in the ControllersBlogsController.cs file within the Comments() method.

3. Add the blog archive list using the code shown in Listing 2-28 to the _BlogNavBar.cshtml file.

LISTING 2-28: Display Dynamic Blog Archive to the BlogNavBar Partial View

@foreach (MVC.Models.BlogArchives blogArchive in Model.blogArchiveList)
{                   
 <table>
  <tr>
   <td> 
     @blogArchive.Year 
     @blogArchive.Month 
     (@Html.ActionLink(blogArchive.Count, "ArchiveList", "Blogs"))
   </td>
  </tr>
 </table>                                    
}

NOTE The ArchiveList view is created in the next section. If you click the link created in Listing 2-27, you will receive an error until the next few steps are completed.

The previous listing segment loops through the List<BlogArchives> blogArchiveList collection located in the BlogNavBarElements class. The sample data adds to the sample database when the code in the Global.asax.cs file is uncommented. It sets the creation date to two months in advance and two months previous to the current date. Therefore, the provided sample data always includes four months of data. Figure 2-17 illustrates an example of the populated blogArchiveList collection in Visual Studio 2012 running in debug mode.

The BlogNavBar partial view is complete. You can now include it into any of the current web pages or to future additions using the following code snippet. The code has been extended to include the ViewData parameter, which was not present the first time you used this code snippet for testing (refer to Listing 2-24).

@Html.Partial("_BlogNavBar", ViewData["blognavbar"])

In the following sections, you can add the BlogNavBar to the Archive List web page, and also create an example blog post.


NOTE The creation of the Reviews, News, Help, Comments, and Advanced C# web pages are not discussed here. They are, however, part of the downloadable ASP.NET MVC 4 sample website (Chapter 2 - MVC.zip) available on Wrox.

CREATING THE ARCHIVE LIST WEB PAGE

The Archive List page is the page that renders the list of blogs written during a selected year and month. Recall from the _BlogNavBar.cshtml partial view that the count value of the dynamic data in the blog.Archive() section is a link to the Archive List. The Archive List receives the year and month as parameters, which are then used as constraints in the NHibernate database query.

To migrate the Archive List ASP.NET web page to ASP.NET MVC 4, you need to perform the following actions (discussed in greater detail in the following sections):

  • Create the ArchiveList.cshtml view in the ViewBlogs directory.
  • Add the ArchiveList() method to the ControllersBlogController.cs file.
  • Create and implement a custom MapRoute.
  • Use LINQ to NHibernate to retrieve strongly typed data from the database using the provided parameters.
  • Use ViewData and ViewBag containers to store and return the data to the view.
  • Modify the ArchiveList.cshtml and_BlogNavBar.cshtml views to display and link to the data.

Creating the ArchiveList.cshtml View

The ArchiveList if the page that displays the list of blogs matching the Year and Month parameters when being accessed from the BLogNavBar or which match the Cloud Tag value. For example, if NHibernate is selected from the tag cloud, then all blogs written about NHibernate is retrieved from the database and presented to the website visitor.

To add a new view named ArchivetList.cshtml, follow these simple steps:

1. Right-click the ViewsBlogs directory, and then click Add ⇒ View.
2. In the Add View window, name the view ArchiveList, leaving the remaining default setting as they are.
3. Click the Add button.

If you have downloaded the example code (found in the Chapter 2 - MVC.zip file), you can look at some of the other views and copy/paste much of the format for this view. For example, the @section featured is standard across the entire website and can be copied from any of the existing views.

Adding the ArchiveList() Action Result Method

From a manageability and maintenance perspective, it is a good idea to group related source files into better-structured subdirectories. Although the following is a small example, if you write a larger website, adding all your models, views, and controllers to a single directory would soon become unmanageable. Therefore, this example attempts to place everything directly related to a blog into the ViewBlogs directory and the ControllersBlogsController.cs file. To do so, follow these steps:

1. Add the ArchiveList() to the ControllersBlogControllers.cs file, as shown in Listing 2-29.

LISTING 2-29: The ArchiveList() Action Result Method

public ActionResult ArchiveList()
{
  return View();
}
Now is a good time to test and make sure that the ArchiveList.cshtml view is how you want to style it and that the ArchiveList() method is wired correctly.
2. Select F5 and navigate to a page where the BlogNavBar is visible.
3. Scroll down to the blog.Archive() section, and click one of the counts in the archive list. Figure 2-18 illustrates what you are looking for.

Create and Implement a Custom MapRoute

To implement the MapRoute, follow these steps:

1. Open the Global.asax.cs file, and add a method called RegisterRoutes(), which resembles that shown in Listing 2-30.

LISTING 2-30: The Custom MapRoute for the ArchiveList Link

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  routes.MapRoute(
      "Blogs",
      "Blogs/ArchiveList/{Year}/{Month}",
      new { controller = "Blogs", action = "ArchiveList", Year = "", Month = "" }
      );
}
The MapRoute creates a map that looks for a pattern like Blogs/ArchiveList/2014/July, for example.
2. To activate the custom MapRoute, add the following code snippet to the Application_Start() method also located in the Global.asax.cs file. A good place to put it is directly after the default generated code and before the previously added code that configured NHibernate.
RegisterRoutes(RouteTable.Routes);

Retrieving the Archive Blog Data with LINQ to NHibernate

The query in this section extracts the blogs written in the selected time frame. Because the result of this query is mapped directly to the Blog class, meaning it is strongly typed, a LINQ query is an option, unlike when the query returns an untyped result.

There are two actions required to extract the data and return it to the view for presentation. Both are performed in the BlogsController.cs file:

  • Create a method called GetArchiveList() that executes the LINQ to NHibernate query.
  • Modify the ArchiveList() action result method to return the data to the view.

To retrieve the archive blog data, follow these steps:

1. Open the ControllerBlogsController.cs, and create a method called GetArchiveList(), as shown in Listing 2-31. The method creates the NHibernate session and transactions, and then uses LINQ to NHibernate to extract the blogs from the database that matches the constraints in the where clause.

LISTING 2-31: The GetArchiveList() Method

using NHibernate.Linq;
 
public IList<Blog> GetArchiveList(int Year, int Month)
{
  using (ISession session = MvcApplication.SessionFactory.OpenSession())
  using (ITransaction transaction = session.BeginTransaction())
  {
    IList<Blog> blogs = (from b in session.Query<Blog>()
                         where b.CreationDate.Year == Year && 
                               b.CreationDate.Month == Month
                         select b).ToList<Blog>();
 
    return blogs;
  }
}

NOTE Don’t forget to add the following statement: using NHibernate.Linq;.

2. In the same ControllerBlogsController.cs file, modify the existing ArchiveList() method so that it resembles that shown in Listing 2-32.

LISTING 2-32: The Updated ArchiveList() Method That Includes Year and Month Parameters

public ActionResult ArchiveList(string Year, string Month)
{
  int iYear = Convert.ToInt32(Year);
  int iMonth = GetMonth(Month);
 
  ViewBag.Year = Year;
  ViewBag.Month = Month;
 
  ViewData["blogarchives"] = GetArchiveList(iYear, iMonth);
  BlogNavBarElements blogElements = new BlogNavBarElements();
  ViewData["blognavbar"] = blogElements.GetBlogNavBarContent();
  return View(ViewData);
}

The first action taken in this method is to convert the Year and Month into integers. This is required because the b.CreationDate.Year and b.CreationDate.Month in Listing 2-30 are integers and, therefore, need to match types.


NOTE The GetMonth() method is not shown here. However, it is a SWITCH statement that checks the month name and changes it to the number of that month.

Next, the ViewBag container is used to pass the Year and Month passed into this method. They are passed back to the view and are used in the presentation of the archive list. Lastly, a call to the GetArchiveList() and the GetBlogNavBarContent() methods to populate the ViewData containers is performed.

Modifying the Views to Display and Link to Data

To finish up this section, the last action is to modify the ArchiveList and BlogNavBar views and present the data. You can do this by following these steps:

1. Modify the Html.ActionLink in the ViewsShared\_BlogNavBar.cshtml file, added previously in Listing 2-28 so that is resembles the code shown in Listing 2-33.

LISTING 2-33: The _BlogNavBar partial view custom MapRoute link

@blogArchive.Year @blogArchive.Month (@Html.ActionLink(blogArchive.Count,
                                                       "ArchiveList",
new { controller = "Blogs", 
      action = "ArchiveList", 
      Year = @blogArchive.Year, 
      Month = @blogArchive.Month 
}))
2. Add the code shown in Listing 2-34 to the ViewsBlogsArchiveList.cshtml below the @section feature added previously.

LISTING 2-34: Present the Data in the ArchiveList.cshtml View

<table>
 <tr>
  <td style="vertical-align: top; width: 900px">
   <table>
    <tr>
     <th>Archive List for @ViewBag.Month - @ViewBag.Year</th>
      </tr>
      <tr><td>&nbsp</td></tr>
      @foreach (var item in ViewData["blogarchives"] 
                                     as IEnumerable<MVC.Models.Blog>)
      {
      <tr>
       <td>
        <a href="http://www.thebes..eworld.com/@item.Type/@item.FileName">
                                                           @item.Title</a> 
       </td>      
      </tr>
      }
     </table>
    </td>
    <td style="vertical-align: top">
           @Html.Partial("_BlogNavBar", ViewData["blognavbar"])</td>
    </tr>
</table>

Now you can run the ASP.NET MVC 4 project, and click the links to see the list of blogs for that time period rendered as hyperlinks. The Archive List is complete. Take a breath and a break. Then continue on with the next section where a sample blog is migrated from ASP.NET to ASP.NET MVC 4.

MIGRATE A BLOG ENTRY FROM ASP.NET WITH FEEDBACK FORM AND COMMENT LIST

It is true that storing the blog contents in the database and retrieving them dynamically would be a little easier than having to create a blog page per blog. This was for Search Engine Optimization (SEO) reasons. When the search engine robots browse your website, they do not get access to all your content in your database. Therefore, creating a single search engine optimized page per blog gets more visibility to your blog. Plus, it makes you implement things such as Master Pages, partial views, and user controls, which are fun and useful.

The steps to migrate a blog from the ASP.NET sample website to an ASP.NET MVC 4 page follow:

  • Create the blog view and add the static content.
  • Add a generic action result method to the ControllerBlogController.cs file called BlogLocator.
  • Create a method to retrieve the details of the blog from the database using the ID.
  • Update the sample blog link to BlogsBlogLocator{id}.
  • Create a shared partial view for the blog comments and existing comment list.
  • Add a partial view to the sample blog.

The example blog for this ASP.NET MVC 4 sample website is titled “Using the as keyword versus boxing.” To create the view for this blog, do the following:

1. Right-click the ViewsBlogs directory, and then click Add ⇒ View.
2. For the view name enter Using-the-as-keyword-versus-boxing and then click the Add button.
3. As you did with other static content for the web pages in this example, open one of the other files and copy/paste the content.
4. After you add the static content, add the reference to the ViewShared\_BlogNavBar.cshtml partial view and continue to the next section, as shown in the following code snippet.
@Html.Partial("_BlogComments")

Adding a Controller to Manage Requests to All Blogs

Although there is only a single blog provided for this example, the logic created for the following exercise supports n number of blogs. This is because you use the Controller created in the ControllerBlogController.cs file for all blogs hosted on the website. To manage requests, add the method shown in Listing 2-35 to the ControllerBlogController.cs file.

LISTING 2-35: A Generic Controller action Method

public ActionResult BlogLocator()
{
  BlogNavBarElements blogElements = new BlogNavBarElements();
  ViewData["blognavbar"] = blogElements.GetBlogNavBarContent();
  return View(ViewData);
}

Creating a Method to Retrieve Blog Details

The code shown in Listing 2-34 currently contains only the logic to populate and return the dynamic content required for the BlogNavBar partial view. Now make changes to the method from Listing 2-34, and add an additional method to dynamically retrieve the selected blog. Listing 2-36 shows the method you use to retrieve the blog from the database using the ID.

LISTING 2-36: The GetBlogDetails Method

public Blog GetBlogDetails(int Id)
{
  using (ISession session = MvcApplication.SessionFactory.OpenSession())
  using (ITransaction transaction = session.BeginTransaction())
  {
    return session.Get<Blog>(Id);
  }
}

To retrieve blog details, add the GetBlogDetails() method to the ControllerBlogController.cs file. When added, modify the BlogLocator() method shown previously in Listing 2-34 so that it resembles that shown in Listing 2-37.

LISTING 2-37: The Updated BlogLocator ActionResult Method

public ActionResult BlogLocator(int Id)
{
  Blog blog = GetBlogDetails(Id);
 
  BlogNavBarElements blogElements = new BlogNavBarElements();
  ViewData["blognavbar"] = blogElements.GetBlogNavBarContent();
  return View(blog.FileName, ViewData);
}

The changes to the BlogLocator() method shown in Listing 2-37 include

  • Adding a parameter to the method called Id. The Id will be part of the URL, for example ~/Blogs/BlogLocator/100, where 100 is the ID of the blog.
  • Adding a call to the GetBlogDetails() method, which passes the Id.
  • Modifying the parameters of the View() method to include the View name. In this example, the name is Using-the-as-keyword-versus-boxing, which matches the name of the ViewBlogs Using-the-as-keyword-versus-boxing.cshtml view and is also the value of the blog.FileName property.

Updating the Example Blog Link

Recall from previous section “Modify the ArchiveList and BlogNavBar Views to Display and Link to the Data” that the links to the blogs were dynamically generated based on the details of the List<Blog> collection. Now return to the ViewsBlogsArchiveList.cshtml view, and modify the <a href> value within the foreach statement so that is resembles the following code snippet.

<a href="/Blogs/BlogLocator/@item.Id">@item.Title</a>

Blogs is the controller, BlogLocator is the action, and the @item.Id is the ID. When the page is generated and the link is clicked, the request is routed to the correct view, based on the Id and the data retrieved from the database.

This is a good point to run the project and test if the previous methods and views work. After the tests are successful, move to the next section where adding a feedback form and a list of blog comments is covered.

Creating a Shared Partial View for the Blog

For this example, the form that enables visitors to add a comment to the blog and the comments themselves are published on each blog. Instead of performing a cut and paste of the code onto each blog, this is another good situation for using a partial view. The partial view enables the creation of the view once and then it can be included into any other required view. To create a shared partial view, follow these steps:

1. To create a partial shared view, right-click the ViewsShared directory, and then click Add ⇒ Views. The Add View dialog appears.
2. Add _BlogComments as the view name, and select the Create as a Partial View check box and click the Add button.
In this example, the contents of the partial view are the form that enables a visitor to add a comment and the list of previously added comments. Listing 2-38 illustrates the most important code segments contained in the ViewsShared\_BlogComments.cshtml file.

NOTE The code to insert the comment into the database is not provided in this example or in the sample ASP.NET MVC 4 website. After you complete this chapter, try to modify the code and perform the action yourself.

LISTING 2-38: Code Segment to List the Comments for a Specific Blog

@model IList<MVC.Models.Comments>
<table>
 @foreach (var comment in ViewData["comments"] as IEnumerable<MVC.Models.Comments>)
{
 <tr>
  <td><b>@comment.Subject</b> - @comment.Comment</td>
 </tr>
 }
</table>
3. Add the code shown in Listing 2-39 to the ControllerBlogController.cs file. The code retrieves all the comments for a given blog ID.

LISTING 2-39: The GetCommentsList Method with Id

public IList<Comments> GetCommentsList(int Id)
{
  using (ISession session = MvcApplication.SessionFactory.OpenSession())
  using (ITransaction transaction = session.BeginTransaction())
  {
    Blog blog = session.Get<Blog>(Id);
    IList<Comments> comments =  blog.comments.ToList<Comments>();
    return comments;
  }
}

NOTE Developers who know about NHibernate’s Lazy Loading capabilities is probably asking why it was not used in this example. Unfortunately, due to the stateless nature of ASP.NET MVC and ASP.NET for that matter, the use of that capability in this context is not possible. The NHibernate implementation for this example is “Session per Database Transaction” and therefore there is no persisted session.

4. Add the following code snippet to the BlogLocator() method in the ControllerBlogController.cs, as shown previously in Listing 2-34, before the View is returned.
ViewData["comments"] = GetCommentsList(Id);

Adding Partial View to the Blog

The final action for this chapter is to add the ViewShared\_BlogComments.cshtml partial view to the sample blog. To do so, follow these steps:

1. Open the sample blog located in the ViewsBlogs directory called Using-the-as-keyword-versus-boxing.cshtml.
2. Navigate to a location under the content, and add the following code snippet.
@Html.Partial("_BlogComments", ViewData["comments"])
3. Run and test the ASP.NET MVC 4 project, clicking the first blog on the list and viewing the form and comments. You can also navigate to any page linking to the Archive List. On the Archive List the link to this sample blog is hard-coded.

SUMMARY

This chapter covered a lot of topics, technologies, and features. You started with an ASP.NET website using ADO.NET and ended with an ASP.NET MVC 4 project using NHibernate. That is a significant change and something you can be proud of after you complete it.

The most important aspects to remember from this chapter are that when you change, change as much as reasonably possible so that you not only make advancements in technology, but you also make advancements in your product and innovate. When this chapter was started, a decision was made to keep the same look and feel in the ASP.NET MVC 4 project as existed on the ASP.NET website. After some effort, the decision was made to change the look and feel of the website to be more like the ASP.NET MVC 4 project. By doing this, it not only updated the website from a technical perspective but also from a product perspective.

From a technical perspective, the configuration and implementation of NHibernate and the concepts around ViewData, ViewBag and how you use them to pass the models from the controller to the view are an important learning point for this chapter. Also, the creation of custom MapRoutes and partial views, which are similar to the ASP.NET user controls, give you a better grasp of what you can do with an ASP.NET MVC 4 website.

In the next chapter, you receive a review of the ASP.NET website and ASP.NET MVC 4 project from a performance perspective and gain some tips on how to optimize them for performance.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset