IN THIS CHAPTER
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.
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.
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:
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.
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.
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:
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:
The following sections cover all these steps in greater detail.
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; }
}
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;
}
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>
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);
}
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.
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).
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:
@Html.ActionLink("C# Fundamentals", "Index", "CSharpFundamentals")
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:
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.
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.
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:
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.
The installation process prompts the user with many options. To complete the installation process, follow these steps:
To create the database, first, you need to open the Server Explorer in Visual Studio and then follow these steps:
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.
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:
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:
At this point, NHibernate is installed and ready for usage.
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:
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; }
...
}
}
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;
}
Listing: 2-11: Executing the ConfigureNHibernate() Method
using NHibernate.Context;
var configuration = ConfigureNHibernate();
configuration.CurrentSessionContext<WebSessionContext>();
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; }
}
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; }
}
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))));
}
}
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);
}
}
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) });
}
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();
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
}
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.
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:
The following actions are required to complete the creation of the BlogNavBar:
The sections further detail these general steps to get your partial view up and running.
You can easily add the partial view to the _BlogNavBar.cshtml by following these steps:
Figure 2-15 illustrates the values for the creation of this partial 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.
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:
LISTING 2-19: The Default ActionResult Method for the netFundamentals.cshtml View
public ActionResult netFundamentals()
{
return View();
}
@Html.ActionLink(".NET Fundamentals", "netFundamentals", "CSharpFundamentals")
@Html.Partial("_BlogNavBar")
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.
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:
You can begin the retrieval of this blog archive list by following these steps:
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; }
}
LISTING 2-22: The BlogArchives Class
public class BlogArchives
{
public string Month { get; set; }
public string Year { get; set; }
public string Count { get; set; }
}
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;
}
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.
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.
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());
}
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());
}
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.
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.
@model MVC.Models.BlogNavBarElements
Blogs: @Html.ValueFor(b => b.blogCount)
Comments: @Html.ActionLink(Model.commentsCount.ToString(), "Comments", "Blogs")
Fundamentals: @Html.ActionLink
(Model.fundamentalsCount.ToString(), "Index", "CSharpFundamentals")
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>
}
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.
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):
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:
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.
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:
LISTING 2-29: The ArchiveList() Action Result Method
public ActionResult ArchiveList()
{
return View();
}
To implement the MapRoute, follow these steps:
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 = "" }
);
}
RegisterRoutes(RouteTable.Routes);
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:
To retrieve the archive blog data, follow these steps:
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;
}
}
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.
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.
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:
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
}))
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> </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.
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:
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:
@Html.Partial("_BlogComments")
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);
}
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
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.
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:
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>
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;
}
}
ViewData["comments"] = GetCommentsList(Id);
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:
@Html.Partial("_BlogComments", ViewData["comments"])
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.