How to create a simple blog using ASP.NET MVC - Part I

Table of Contents

1. Introduction

There are different ways we can learn a technology: by reading books, by attending conferences, by doing samples and more. I think one of the best way is by creating something useful for us using the technology. One of the useful thing that you could create easily is a blog. In this multi-part series, we are going to learn ASP.NET MVC step by step by creating a blog from scratch.

JustBlog

JustBlog

To keep things simple we are not going to build the commenting system instead we are going to use Disqus. I encourage you to build the commenting system by yourself and that would be a good exercise for you.

We are going to use ASP.NET MVC 4 to develop the application. I'm not good at Entity Framework and we are going to use Fluent NHibernate/NHibernate combo to build the data-access system. You could use Entity Framework if you like. Finally, we are going to use Ninject for dependency injection because of it's simplicity.

In the first part of the series we are going to build the basic infrastructure of the blog. We are going to create the necessary model classes, data access components, controllers and views. At the end of this part we have a working blog where we can see the latest posts, read a complete post, browse posts based upon a category or tag and even search for interested posts.

In the second part, we are going to build an admin console to manage our posts, tags and categories.

In the final part we are going to integrate Disqus commenting system with our blog. We also see about integrating AddThis, FeedBurner for sharing and subscriptions. Last but not least, we also take necessary measurements for SEO.

2. Technologies

→ ASP.NET MVC 4.0

→ Fluent NHibernate and NHibernate

→ SQL Server 2012

→ Ninject

→ jQuery

3. Part I - Create the basic infrastructure. Create the necessary model classes, data access components, controllers and views.

Let's see the user stories that we are going to complete in this part.

3.1 User Stories

Story #1 - Display the latest blog posts

Story #2 - Display posts based on category

Story #3 - Display posts based on tag

Story #4 - Search posts

Story #5 - Display the details of a single post

Story #6 - Display the post categories in a widget

Story #7 - Display the post tags in a widget

Story #8 - Display the latest posts in a widget

4. Story #1 - Display the latest blog posts

What we are going to accomplish in this story is to read the blog posts from database and display them in a view.

Before implementing the story we have to make up the basic setup ready. We have to create the solution and necessary projects.

Create an empty solution with the name JustBlog.

Create empty solution

Create empty solution

Create a MVC 4 web application with the same name as the solution i.e. JustBlog. In the "Select a template" window select "Empty template".

Create MVC 4 application

Create MVC 4 application

Select Empty Template

Select Empty Template

Create a class library and name it as JustBlog.Core. It's a best practice to keep the domain classes and the data-access components in a separate project and that would helps us manage the application easier in terms of development, testing and deployment. Don't forget to add reference to JustBlog.Core in JustBlog.

Our basic setup is ready. The solution will looks like below after adding the necessary projects.

Solution Explorer

Solution Explorer

This is a little bigger user story. We are going to do the initial data-access and DI (Dependency Injection) configuration work as part of this story.

Let's break our user story into smaller tasks and that helps us to implement it easily.

1. Create domain classes
2. Configure Fluent NHibernate and NHibernate
3. Create mapping classes, data access classes and methods
4. Configure Ninject for core project
5. Configure Ninject for MVC
6. Create controller and actions
7. Create view

4.1 Create domain classes

Create a new folder called Objects in the JustBlog.Core project to place the domain classes. We need to create three domain classes for our blog: Post, Category and Tag. Each Post belongs to a single Category and it can be labelled with many Tags. Between Post and Category the relationship is many-to-one and between Post and Tag the relationship is many-to-many.

The relationship between the classes is illustrated by the below diagram.

Class Diagram

Class Diagram

Here is our Post class.

namespace JustBlog.Core.Objects
{
    public class Post
    {
	    public virtual int Id
	    { get; set; }

	    public virtual string Title
	    { get; set; }

	    public virtual string ShortDescription
        { get; set; }

	    public virtual string Description
	    { get; set; }

	    public virtual string Meta
	    { get; set; }

	    public virtual string UrlSlug
        { get; set; }

	    public virtual bool Published
	    { get; set; }

	    public virtual DateTime PostedOn
	    { get; set; }

	    public virtual DateTime? Modified
	    { get; set; }

	    public virtual Category Category
	    { get; set; }

	    public virtual IList<Tag> Tags
	    { get; set; }
    }
}

Listing 1. Post model

Most of the properties are self-explanatory. The UrlSlug property is an alternate for the Title property to use in address.

For example, if we have a post with title "Advanced Linq in C#" published on August 2010, we are going to create the urls such that the post can be accessed by the address http://localhost/archive/2010/8/Advanced Linq in C#. The post title may contain special characters (in this example there is a "#") and not all the servers can handle those requests having special characters. Instead of using the Title property directly in the url we have to use some alternate text that resembles the post title and that is called as an URL Slug.

In above case, instead of using "Advanced Linq in C#" in url we are going to use an user-friendly text(slug) like "advanced_linq_in_csharp", therefore the address will be http://localhost/archive/2010/8/advanced_linq_in_csharp. In Part II, we will see how to create slug automatically from the post title.

URL Slug

A URL slug is a SEO- and user-friendly string-part in a URL to identify, describe and access a resource. Often the title of a page/article is a valid candidate.

The Meta property is used to store the metadata description for the post and it's used for SEO. An interesting thing to note down is all the properties are marked as virtual and it's quite important as well. NHibernate creates a runtime proxy for this class and for that all the properties needs to be virtual.

The Category and Tag classes are simple as shown below. We have used the UrlSlug property for the same reason that we discussed above.

namespace JustBlog.Core.Objects
{
    public class Category
    {
	    public virtual int Id
	    { get; set; }

	    public virtual string Name
	    { get; set; }

	    public virtual string UrlSlug
        { get; set; }

	    public virtual string Description
	    { get; set; }

	    public virtual IList<Post> Posts
	    { get; set; }
    }
}

Listing 2. Category model

namespace JustBlog.Core.Objects
{
    public class Tag
    {
	    public virtual int Id
	    { get; set; }

	    public virtual string Name
	    { get; set; }

	    public virtual string UrlSlug
        { get; set; }

	    public virtual string Description
	    { get; set; }

	    public virtual IList<Post> Posts
	    { get; set; }
    }
}

Listing 3. Tag model

4.2 Configure Fluent NHibernate and NHibernate

We are going to use NHibernate along with Fluent NHibernate for database access. NHibernate is an ORM tool much like Entity Framework where the relationships between classes to tables are mapped through xml files. Fluent NHibernate is an extension for NHibernate which replaces xml files with classes. Mapping through classes is much easier than xml files.

We can easily add references to NHibernate and Fluent NHibernate assemblies through the Nuget Package Manager Console.

Open up the Package Manager from Tools -> Library Package Manager -> Package Manager Console.

Execute the following command from the console.

PM> Install-Package FluentNHibernate

Installing Fluent NHibernate package will install the necessary assemblies. If the installation is successful we will see the following assemblies added to the References.

FluentNHibernate
NHibernate
Iesi.Collections

4.3 Create data access classes and methods

Next thing we have to do is create the necessary mapping classes. A mapping class is used to map a class and it's properties to table and columns. Create a new folder called Mappings in the JustBlog.Core project to keep all the mapping classes.

This is the mapping class for Post.

using FluentNHibernate.Mapping;
using JustBlog.Core.Objects;

namespace JustBlog.Core.Mappings
{
    public class PostMap: ClassMap<Post>
    {
	    public PostMap()
	    {
		    Id(x => x.Id);

		    Map(x => x.Title)
			    .Length(500)
			    .Not.Nullable();

		    Map(x => x.ShortDescription)
			    .Length(5000)
			    .Not.Nullable();

		    Map(x => x.Description)
			    .Length(5000)
			    .Not.Nullable();

		    Map(x => x.Meta)
			    .Length(1000)
			    .Not.Nullable();

		    Map(x => x.UrlSlug)
			    .Length(200)
			    .Not.Nullable();

		    Map(x => x.Published)
			    .Not.Nullable();

		    Map(x => x.PostedOn)
			    .Not.Nullable();

		    Map(x => x.Modified);

		    References(x => x.Category)
			    .Column("Category")
			    .Not.Nullable();

		    HasManyToMany(x => x.Tags)
			    .Table("PostTagMap");
	    }
    }
}

Listing 4. PostMap class

To create a mapping class we should inherit it from the Fluent NHibernate's generic class ClassMap. All the mappings has to be done in the constructor.

The Id extension method is used to represent the property name that has to be set as the primary key column of the table. As default, Fluent NHibernate assumes the table name as same as the class name and column name as same as the property name. If the table name is different then we should map the table with the class using the Table extension method.

Ex.

Table("tbl_posts");

Listing 5. Table extension method

The Map extension method is used to map a property with a table column. While mapping a property we can specify the size of the column, whether it's nullable or not and other details. If the generated column name has to be different from the property name then we should pass the column name using the Column extension method.

Ex.

Map(x => x.Title).Column("post_title")

Listing 6. Map extension method

The References method is used to represent the many-to-one relationship between Post and Category through a foreign key column Category in the Post table. HasManyToMany method is used to represent many-to-many relationship between Post and Tag and this is achieved through an intermediate table called PostTagMap. You can learn more details about Fluent NHibernate and it's extension methods from here.

The mapping classes of Category and Tag are pretty much same except the relationship they have with Post. Category has one-to-many relationship while Tag has many-to-many relationship with Post.

namespace JustBlog.Core.Mappings
{
    public class CategoryMap: ClassMap<Category>
    {
	    public CategoryMap()
	    {
		    Id(x => x.Id);

		    Map(x => x.Name)
			    .Length(50)
			    .Not.Nullable();

		    Map(x => x.UrlSlug)
			    .Length(50)
			    .Not.Nullable();

		    Map(x => x.Description)
			    .Length(200);

		    HasMany(x => x.Posts)
			    .Inverse()
			    .Cascade.All()
			    .KeyColumn("Category");
	    }
    }
}

Listing 7. CategoryMap class

namespace JustBlog.Core.Mappings
{
    public class TagMap: ClassMap<Tag>
    {
	    public TagMap()
	    {
		    Id(x => x.Id);

		    Map(x => x.Name)
			    .Length(50)
			    .Not.Nullable();

		    Map(x => x.UrlSlug)
			    .Length(50)
			    .Not.Nullable();

		    Map(x => x.Description)
			    .Length(200);

		    HasManyToMany(x => x.Posts)
			    .Cascade.All().Inverse()
			    .Table("PostTagMap");
	    }
    }
}

Listing 8. TagMap class

We are going to use the Repository pattern for database access. We are using this pattern to decouple the data access code from our controllers and that helps us to simplify unit testing our controllers. The core of the repository pattern is an interface which contains the definitions for all the data-access methods.

Let's create an interface IBlogRepository in the JustBlog.Core project with couple of method definitions.

namespace JustBlog.Core
{
    public interface IBlogRepository
    {
	    IList<Post> Posts(int pageNo, int pageSize);
	    int TotalPosts();
    }
}

Listing 9. IBlogRepository

The Posts method is used to return the latest published posts based on pagination values. The TotalPosts method is used to return the total no. of published posts. We are going to fill the interface with more methods in the upcoming stories.

Let's create a class with name BlogRepository in the core project and implement the interface.

using JustBlog.Core.Objects;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernate.Transform;
using System.Collections.Generic;
using System.Linq;

namespace JustBlog.Core
{
    public class BlogRepository: IBlogRepository
    {
	    // NHibernate object
	    private readonly ISession _session;

	    public BlogRepository(ISession session)
	    {
		    _session = session;
	    }

	    public IList<Post> Posts(int pageNo, int pageSize)
	    {
        var posts = _session.Query<Post>()
                              .Where(p => p.Published)
                              .OrderByDescending(p => p.PostedOn)
                              .Skip(pageNo * pageSize)
                              .Take(pageSize)
                              .Fetch(p => p.Category)
                              .ToList();

        var postIds = posts.Select(p => p.Id).ToList();

        return _session.Query<Post>()
              .Where(p => postIds.Contains(p.Id))
              .OrderByDescending(p => p.PostedOn)
              .FetchMany(p => p.Tags)
              .ToList();
	    }

	    public int TotalPosts()
	    {
		    return _session.Query<Post>().Where(p => p.Published).Count();
	    }
    }
}

Listing 10. BlogRepository

All the calls to the database has to be made through the NHibernate's ISession object. When we read the collection of posts using ISession, the dependencies Category and Tags are not populated by default. Fetch and FetchMany methods are used to tell NHibernate to populate them eagerly.

In the Posts method, we've queried database twice to get the posts because we've to eager load all the associated tags. We can't use FetchMany along with Skip and Take methods in the Linq query. So, first we've fetched all the posts then from their ids we've queried again to get them with their tags. Please see this thread for more information about this issue.

NHibernate ISession

ISession is the persistence manager interface that is used to store and retrieve entities to and from the database.

4.4 Configure Ninject for JustBlog.Core project

Dependency injection (DI) helps to avoid instantiating the concrete implementations of dependencies inside a class. These dependencies are usually injected to a class through constructor but sometimes through properties as well. One of the main advantage of dependency injection is unit testing and we will see that when we write unit tests for controllers in Part II.

There are many frameworks available to simplify dependency injection like Castle Windsor, Unity, Autofac, StructureMap, Ninject etc. We have picked Ninject because it's easy to use.

We can install Ninject in the JustBlog.Core project by executing the following commands in Package Manager Console.

PM> Install-Package Ninject
PM> Install-Package Ninject.Web.Common

If the commands are executed successfully we will see both the Ninject and Ninject.Web.Common assemblies added to the project. Along with the assemblies a class file with name NinjectWebCommon.cs also added in the App_Start folder. I'll explain the reason why we need to install the extension Ninject.Web.Common soon.

We can configure Ninject in web application using two approaches, either using Global.asax.cs or through App_Start. We are going to use the first approach so please delete the NinjectWebCommon.cs file from the App_Start folder and also remove the unncessary references to WebActivator and Microsoft.Web.Infrastructure assemblies from the project.

The core functionality of any DI framework is to map the interfaces with concrete implementations. Mapping an interface with a particular implementation is called as binding. We can group a set of bindings related to a particular module in Ninject using Ninject Modules. All bindings and modules are loaded to the core component of Ninject called Kernel. Whenever the application needs an instance of a concrete class that implements the interface, it is the Kernel that provides one.

Ninject Module

An Ninject module is used to group the mappings/bindings related to a particular module in a single class.

The BlogRepository class has a dependency with Nibernate's ISession. To create an instance of ISession we need the help of another Nibernate interface called ISessionFactory. Let's create an ninject module class with name RepositoryModule that bind both these interfaces in the JustBlog.Core project.

NHibernate ISessionFactory

Unlike ISession we need a single instance of ISessionFactory throughout the application.

using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using JustBlog.Core.Objects;
using NHibernate;
using NHibernate.Cache;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Common;

namespace JustBlog.Core
{
    public class RepositoryModule: NinjectModule
    {
	    public override void Load()
	    {
		    Bind<ISessionFactory>()
			    .ToMethod
			    (
				    e =>
					    Fluently.Configure()
					    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c =>
						    c.FromConnectionStringWithKey("JustBlogDbConnString")))
					    .Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>())
					    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Post>())
					    .ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(true, true, false))
					    .BuildConfiguration()
					    .BuildSessionFactory()
			    )
			    .InSingletonScope();

		    Bind<ISession>()
			    .ToMethod((ctx) => ctx.Kernel.Get<ISessionFactory>().OpenSession())
			    .InRequestScope();
	    }
    }
}

Listing 11. RepositoryModule

To create an Ninject module we have to inherit from the abstract class NinjectModule and implement the Load method. In the Load method, we have mapped (binded) both the interfaces to methods using the Bind method.

At a simple level, the Bind method is used to map an interface with a class that implements it.

Ex.

Bind<IFoo>().To<Foo>();

Listing 12. Binding interface to class

You could also map the interface with a method which instantiate and return an implementation of the interface. Binding interface to a method is very handy when creating an instance requires additional work.

Bind<IFoo>().ToMethod(c => {
	var foo = new Foo();
	return foo;
});

Listing 13. Binding interface to method

We have used the Fluent API's extension methods to create an instance of ISessionFactory.

	Fluently.Configure()
		.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c =>
			c.FromConnectionStringWithKey("JustBlogDbConnString")))
		.Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>())
		.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Post>())
		.ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(true, true, false))
		.BuildConfiguration()
		.BuildSessionFactory()

Listing 14. Configuring through fluent extension methods

The chained extension methods may look little confusing! The following are the things that we are doing through those methods.

a. Setting the database connection string (Database)
b. Setting a provider to cache the queries (Cache)
c. Specifying the assembly where the domain & mapping classes exists (Mappings)
d. Ask NHibernate to create tables from the classes (ExposeConfiguration)

There are many extensions available with Ninject and one of them is Ninject.Web.Common which contains some common functionalities required for both WebForms and MVC applications. We have used that extension in the above case to set the scope of the ISession to a request level, means until the request completes Ninject uses the same instance of ISession throughout the code. The InRequestScope() extension method lives in the Ninject.Web.Common assembly. We need a single instance of ISessionFactory throughout the application and so we have scoped it to singleton (InSingletonScope()).

4.5 Configure Ninject for MVC project

All the database calls from the controllers are streamlined through the IBlogRepository interface. To inject an instance of a class that implements IBlogRepository to a controller we need to configure Ninject in MVC application as well. There is an extension (Ninject.Mvc3) available to specifically support for MVC applications. We can install it to our MVC project by running the following command.

P> Install-Package Ninject.Mvc3

On successfully executing the command the following assemblies are added to the MVC project.

Ninject
Ninject.Web.Common
Ninject.Web.Mvc

Remove the NinjectWebCommon.cs file from the App_Start folder and derive our global application class from NinjectHttpApplication class and override the CreateKernel method.

using Ninject;
using Ninject.Web.Common;
using System.Web.Mvc;
using System.Web.Routing;

namespace JustBlog
{
    public class MvcApplication : NinjectHttpApplication
    {
	    protected override IKernel CreateKernel()
	    {
		    var kernel = new StandardKernel();

		    kernel.Load(new RepositoryModule());
		    kernel.Bind<IBlogRepository>().To<BlogRepository>();

		    return kernel;
	    }

	    protected override void OnApplicationStarted()
	    {
		    RouteConfig.RegisterRoutes(RouteTable.Routes);
		    base.OnApplicationStarted();
	    }
    }
}

Listing 15. Global Application Class

In the CreateKernel method, we are creating and returning an instance of StandardKernel which is a type of IKernel. IKernel is the core of the application where we specify our bindings and when we need an instance of a mapped interface it is the one that provides.

We are doing couple of important things in the StandardKernel object. First, we are loading an instance of our Repository module that contains all the bindings related to NHibernate interfaces to it and then specify another binding that maps the IBlogRepository to BlogRepository directly to it. Finally the code we have to run at the application startup has to moved to the OnApplicationStarted method.

That's all about the configuration work for Ninject in application. Let's start working on controllers and actions.

4.6 Create controller and actions

So far we have concentrated more time on building the models, data-access classes using NHibernate/Fluent NHibernate and also configuring Ninject for dependency injection. Now it's time to concentrate on building our MVC project.

Let's create a controller by right-clicking at the Controllers folder -> Add -> Controller. Name the controller as BlogController.

Add Controller

Add Controller

Add Blog Controller

Add Blog Controller

Controller

In MVC architecture, Controller is the component that handles requests from clients, process them and provide a response. In ASP.NET MVC application all the controllers are usually placed in the Controllers folder. Each controller has to be suffixed with the name "Controller". The component that responsible for searching controllers looks for classes that derives from the class Controller and whose name ends with "Controller".

Create a constructor that takes IBlogRepository as input parameter. Ninject will take care of providing an instance of BlogRepository to the BlogController while instantiating it.

using JustBlog.Core;
using System.Web.Mvc;

namespace JustBlog.Controllers
{
    public class BlogController
    {
	    private readonly IBlogRepository _blogRepository;

	    public BlogController(IBlogRepository blogRepository)
	    {
		    _blogRepository = blogRepository;
	    }
    }
}

Listing 16. BlogController

Create a method with name Posts that takes an input parameter p for page number,

public ViewResult Posts(int p = 1)
{
	// TODO: read and return posts from repository
}

Listing 17. Posts action

Any public method in a controller can be called as an action. Usually actions returns any type that derives from ActionResult. In the above Posts action, we are going to return a view and for that we have used the ViewResult as return type.

The other types of ActionResult are:

Type Description
PartialViewResult Renders a partial view
RedirectToRouteResult Issues a HTTP 301 or 302 redirection to an action method or specific route entry, generating a URL according to your routing configuration.
RedirectResult Issues a HTTP 301 or 302 redirection to a specified URL.
ContentResult Returns a raw textual data to the browser, optionally setting a content-type header.
FileResult Transmits binary data (such as a file from disk or a byte array in memory) directly to the browser.
JsonResult Renders JSON content to client.
JavaScriptResult Sends a snippet of JavaScript source code that should be executed by the browser.
HttpUnauthorizeResult Set the response HTTP status code to 401 (meaning "not authorized"), which causes the active authentication mechanism (forms authentication or Windows authentication) to ask the visitor to log in.
HttpNotFoundResult Returns a HTTP 404 - Not found error.
HttpStatusCodeResult Returns a specified HTTP code.
EmptyResult Does nothing.
Different types of built-in ActionResults. Source: Pro ASP.NET MVC 3 by Adam & Steven

The parameter p in the Posts action represents the page number. The default value of p is 1 and that represents the first page. Inside the action all we have to do is fetch the latest posts by calling the Posts method of IBlogRepository and feed that to the view.

Along with the latest posts we also need to feed the total no. of posts to the view which is required for showing pagination links in the view. This is a perfect scenario where we can go for View Models.

View Models

View Models are classes specially created for views. They contains properties related to both domain as well as UI.

Create a class with name ListViewModel under the Models folder that wraps both the collection of active posts and their total number.

using JustBlog.Core;
using System.Collections.Generic;

namespace JustBlog.Models
{
    public class ListViewModel
    {
	    public IList<Post> Posts { get; private set; }
	    public int TotalPosts { get; private set; }
    }
}

Listing 18. ListViewModel

Here is the initial implementation of the Posts action.

public ViewResult Posts(int p = 1)
{
	// pick latest 10 posts
	var posts = _blogRepository.Posts(p - 1, 10);

	var totalPosts = _blogRepository.TotalPosts();

	var listViewModel = new ListViewModel
	{
		Posts = posts,
		TotalPosts = totalPosts
	};

	ViewBag.Title = "Latest Posts";

	return View("List", listViewModel);
}

Listing 19. Posts action

We are passing the title that has to be set in the view through the ViewBag. ViewBag is a dynamic wrapper around the ViewData dictionary. Instead of directly instantiating and returning ViewResult we have utilized the built-in helper method called View to accompilsh the job. The first parameter we have passed to the View method is the name of the view and the next parameter is the model.

Passing data to views

ViewBag or ViewData is used to pass data from a controller to a view. They are encouraged to be used when you want to pass small pieces of information to a view and in other cases it's always good to go for strongly-typed views.

Instead of getting the latest posts through _blogRepository in Posts action we can delegate that to the ListViewModel by passing to it.

public class ListViewModel
{
	public ListViewModel(IBlogRepository _blogRepository, int p)
	{
		Posts = _blogRepository.Posts(p - 1, 10);
		TotalPosts = _blogRepository.TotalPosts();
	}

	public IList<Post> Posts { get; private set; }
	public int TotalPosts { get; private set; }
}

Listing 20. ListViewModel

Here is our modified action.

public ViewResult Posts(int p = 1)
{
	var viewModel = new ListViewModel(_blogRepository, p);

	ViewBag.Title = "Latest Posts";
	return View("List", viewModel);
}

Listing 21. Posts action

Our controller and action is ready and it's time to work on the view.

4.7 Create view

I've downloaded a free template for our blog from here, you can download the modified css and images of the template from GitHub or attached source-code.

4.7.1 Setting up the theme and layout

Create a folder with name Content at the root of your MVC project. Under Content, create folders themes > simple. Move the downloaded stylesheet to the simple folder and the images to a new folder images under simple. The MVC project should look like below if you have done everything correctly.

Theme folders

Theme folders

Layout

Like Master Pages in WebForms it's Layouts in MVC. A site can have as many layouts as it needs and they contain the common html content that should appear throughout all the views. Let's create a new folder called Shared under Views. Right click the folder and select "Add View". Type the view name as _Layout and uncheck the "Use a layout or master page". We are going to use Razor view engine for building the view and so make sure the "View engine" dropdown is set to Razor (CSHTML).

Layout

Layout

Replace the contents of _Layout.cshtml as below.

<!DOCTYPE html>

<html>

<head>
  <meta name="viewport" content="width=device-width" />
  <title>@ViewBag.Title</title>

  <!-- Stylesheets -->
  <link rel="stylesheet" type="text/css"
	href="@Url.Content("~/Content/themes/simple/style.css")" />
</head>

<body>
  <div id="main">

    <!-- Header -->
    <div id="header">
      <div id="logo">
        <div id="logo_text">
          <h1><a href="/">Just<span class="logo_colour">Blog</span></a></h1>
          <h2>Writings, Experiments and More...</h2>
        </div>
      </div>

      <!-- Navigation -->
      <div id="menubar">
        <ul id="menu">
          <li><a href="#">Posts</a></li>
          <li><a href="#">Contact</a></li>
          <li><a href="#">About Me</a></li>
        </ul>
      </div>

    </div>

    <!-- Site Content -->
    <div id="site_content">
      @RenderBody()
    </div>

     <!-- Footer -->
    <div id="footer">
      Copyright � @DateTime.Now.Year JustBlog All Rights Reserved
    </div>
  </div>
</body>
</html>

Listing 22. _Layout.cshtml

The layout contain mainly four sections Header, Navigation, Content and Footer. There are couple of interesting things to note down. The ViewBag.Title that we have passed from the controller is used in the <title> section. Next thing is the RenderBody method. The RenderBody method generate the actual content of the view and place it where it's been called.

Setting Layouts for Views

We can set a layout to a view through the Layout property of the view

Ex.
Home.cshtml
@{
   Layout = "~/Views/Shared/_Layout.cshtml";
}

Instead of setting the layout for each view individually we can set that globally using the _ViewStart.cshtml file.

We can set the layout for all the views globally through the _ViewStart.cshtml. As the name this file is executed before calling each view. Create the _ViewStart.cshtml under the Views folder with the following contents.

@{
  Layout = "~/Views/Shared/_Layout.cshtml";
}

Listing 23. _ViewStart.cshtml

We have some work to do in the anchor links in the navigation sections.

Navigation

Navigation

<ul id="menu">
    <li><a href="#">Posts</a></li>
    <li><a href="#">Contact</a></li>
    <li><a href="#">About Me</a></li>
</ul>

Listing 24. Navigation section

Like ASP.NET controls in WebForms, MVC has Html-Helpers. Html-Helpers are light-weight and they are just used to create simple html controls/elements like form, textbox, anchor links etc. The Html-Helpers are available through the Html property in views.

To create an anchor link with name Posts pointing to Posts action, we can do that by,

@Html.ActionLink("Posts", "Posts") // (link name, action)

Listing 25. Html.ActionLink

The above razor statement will generate the following html,

	<a href="/Blog/Posts">Posts</a>

Listing 26. Generated anchor link

Right now we have only the Posts action but in the coming parts we will create actions to display contact and about me pages. After replacing the anchors with Html.ActionLinks the navigation section will be,

<ul id="menu">
    <li>@Html.ActionLink("Posts", "Posts")</li>
    <li>@Html.ActionLink("Contact", "Contact")</li>
    <li>@Html.ActionLink("About Me", "AboutMe")</li>
</ul>

Listing 27. Navigation section

Html-Helpers

The built-in html-helpers also helps to create input controls like textbox, checkbox, dropdowns and even html form.

Ex. @Html.TextBox("Name")

We can even create our own custom html-helpers.

4.7.3 List view

We can create a view for an action by right-clicking anywhere inside the action and selecting "Add View". When we create a view we can strongly tie it with a model and they are called as strongly-typed views. In the "Add View" window, we have a "Create a strongly-typed view" checkbox and once you check that you can select the model through the "Model class" dropdown. Select the ListViewModel in the dropdown and make sure the "Use a layout or master page" checkbox is checked.

Create List View

Create List View

The generated List.cshtml looks like below,

@model ListViewModel

@{
    ViewBag.Title = "List";
}

Listing 28. List.cshtml

The @model directive at the top of the view says that it is strongly typed with the ListViewModel.

Strongly-typed Views

Unlike the normal views which inherit from System.Web.Mvc.ViewPage, a strongly-typed view inherits from System.Web.Mvc.ViewPage<T> and contains a Model property that is of type specified by T. The most important advantage of strongly-typing a view is intellisense support.

The instance of ListViewModel that we are passing from the Posts action can be accessed right through the Model property in the List view.

In our List view, all we have to do is iterate the Posts collection and display them. On displaying each post we have to display the date on which it is posted, description and also the category and tags related to the post.

Let's create a partial view that displays the details of a single post. Partial Views are similar like User Controls in WebForms.

We can create a partial view similar like a view. Right-click at the Shared folder and select Add > View. In the "Add View" window select the Post entity as model and check the "Create as partial view" checkbox. Name the partial view as _PostTemplate.

Create Partial View

Create Partial View

Add the following content to the _PostTemplate partial view. The explanation follows the markup.

@model JustBlog.Core.Objects.Post

<div class="post">
  <div class="post-meta">
    <div class="row">

	  <!--Heading-->
      <div class="post-title">
        <h2>@Html.PostLink(Model)</h2>
      </div>

    </div>

    <div class="row">

	  <!--Category-->
      <div class="post-category">
        <span>Category: </span>@Html.CategoryLink(Model.Category)
      </div>

	  <!--Tags-->
      <div class="post-tags">
        <span>Tags:</span>@Helpers.Tags(Html, Model.Tags)
      </div>

	  <!--Posted date-->
      <div class="posted-date">
        @Model.PostedOn.ToConfigLocalTime()
      </div>

    </div>

  </div>

  <!--Short Description-->
  <div class="post-body">
    @Html.Raw(Model.ShortDescription)
  </div>

  <div class="post-foot">
    @Html.ActionLink("continue...", "post", "blog",
		new
		{
			year = Model.PostedOn.Year,
			month = Model.PostedOn.Month,
			day = Model.PostedOn.Day,
			title = Model.UrlSlug
		},
		new { title = "continue..." }
	)
  </div>
</div>

Listing 29. _PostTemplate.cshtml

We want to display the post heading, category and tags as hyperlinks. It would be better if we create custom html helpers for generating the links.

Links

Links

We have used three custom html helpers (@Html.PostLink, @Html.CategoryLink and @Helper.Tags) that generate anchor tags from the Post, Category and Tags. We could create a custom html helper both by declarative and through code. Code html helpers are better when the generated html is simple but in complex cases it's better to go for declarative helpers. The post heading and category links are simple and so we have used code-based html-helpers but in case of tags it's little complicated and so we have used a declarative one.

Custom Html-Helpers

We can create a custom html-helper by creating an extension method for HtmlHelper class. The HtmlHelper is available as property Html in views.

First create the html-helpers for post heading, category and tag (single) links. Create a new class file ActionLinkExtensions in the root of the MVC project.

using JustBlog.Core.Objects;
using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace JustBlog
{
    public static class ActionLinkExtensions
    {
	    public static MvcHtmlString PostLink(this HtmlHelper helper, Post post)
	    {
		    return helper.ActionLink(post.Title, "Post", "Blog",
			    new {
				    year = post.PostedOn.Year,
				    month = post.PostedOn.Month,
				    title = post.UrlSlug
			    },
			    new {
				    title = post.Title
			    });
	    }

	    public static MvcHtmlString CategoryLink(this HtmlHelper helper,
		    Category category)
	    {
		    return helper.ActionLink(category.Name, "Category", "Blog",
			    new {
				    category = category.UrlSlug
			    },
			    new {
				    title = String.Format("See all posts in {0}", category.Name)
			    });
	    }

	    public static MvcHtmlString TagLink(this HtmlHelper helper, Tag tag)
        {
		    return helper.ActionLink(tag.Name, "Tag", "Blog", new { tag = tag.UrlSlug },
			    new {
				    title = String.Format("See all posts in {0}", tag.Name)
			    });
        }
    }
}

Listing 30. ActionLinkExtensions

Our custom html-helpers uses the built-in ActionLink helper method to creates the link. Alternatively, we can construct the anchor tag inside the code using the built-in TagHelper class as well. The built-in ActionLink helper has many overloaded versions and the one that we have used takes 5 arguments which are described below.

return helper.ActionLink
(
	post.Title, 	// anchor text
	"Post", 		// action name
	"Blog", 		// controller name
	new { 			// route parameters
		year = post.PostedOn.Year,
		month = post.PostedOn.Month,
		title = post.UrlSlug
	},
	new { 			// html attributes
		title = post.Title
	}
);

Listing 31. Custom ActionLink Helper

We will see about the route parameters when we define routes for our action. Both the route parameters and html attributes are passed as anonymous objects.

To use the custom extension methods in views we have to specify the namespace (JustBlog) where they exists either in views using the @using statement or in the web.config file located in the Views folder. By the later approach we can access the extension method throughout all the views.

<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
	<namespaces>
		<add namespace="System.Web.Mvc" />
		<add namespace="System.Web.Mvc.Ajax" />
		<add namespace="System.Web.Mvc.Html" />
		<add namespace="System.Web.Routing" />
		<add namespace="JustBlog"/>
	</namespaces>
</pages>
</system.web.webPages.razor>

Listing 32. Specifying the namespace in View's web.config

For reusable declarative html helpers we have to create a cshtml file in the App_Code folder. Let's create a cshtml file with name Helpers.cshtml under the App_Code folder. The important thing to keep in mind is for cshtml files located other than Views folder the Html and other properties of View are not available, so we have to pass the HtmlHelper as an argument to the helper methods.

@helper Tags(System.Web.Mvc.HtmlHelper htmlHelper,
	IList<JustBlog.Core.Objects.Tag> tags)
{
  foreach (var tag in tags)
  {
    <div class="tags-div">
		@JustBlog.ActionLinkExtensions.TagLink(htmlHelper, tag)
	</div>
  }
}

Listing 33. Helpers.cshtml

We can call the Tags method from any view by [file-name].[method-name] i.e. Helpers.Tags(args).

All the datetimes in the database are going to be stored in the UTC (Coordinated Universal Time) timezone (which we will see in the Part II). The advantage of storing datetimes in UTC timezone is, we can easily convert the dates to a particular timezone. To know more details about this, refer this article. When the datetimes are read from the database we have to convert them to a particular timezone (which will be specified in the configuration) before displaying to the user.

Let's create a class called Extensions which contains an extension method ToConfigLocalTime that converts the datetimes in UTC timezone to the timezone specified in the configuration.

using JustBlog.Core.Objects;
using System;
using System.Configuration;
using System.Web.Mvc;

namespace JustBlog
{
    public static class Extensions
    {
	    public static string ToConfigLocalTime(this DateTime utcDT)
	    {
		    var istTZ = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["Timezone"]);
		    return String.Format("{0} ({1})", TimeZoneInfo.ConvertTimeFromUtc(utcDT, istTZ).ToShortDateString(), ConfigurationManager.AppSettings["TimezoneAbbr"]);
	    }
    }
}

Listing 34. ToConfigLocalTime method

For example, to display all the datetimes in "India Standard Time", we have to specify the following values in the configuration. Click here to know the list of other timezones.

<add key="Timezone" value="India Standard Time" />
<add key="TimezoneAbbr" value="IST" />

Listing 35. Timezone configuration values

Now where-ever we are displaying datetimes we have to call the extension method ToConfigLocalTime on the datetime object.

<!--Posted date-->
<div class="posted-date">
	@Model.PostedOn.ToConfigLocalTime()
</div>

Listing 36. Displaying the posted date

Being our partial view (_PostTemplate) is ready, we can complete our List view. In the List view all we have to do is iterate the posts collection and render each post using the _PostTemplate partial view.

@model JustBlog.Models.ListViewModel

<div id="content">

  <h1>@ViewBag.Title</h1>

  @if (Model.Posts.Count > 0)
  {
    foreach (var post in Model.Posts)
    {
      @Html.Partial("_PostTemplate", post)
    }
  }
  else
  {
    <p>No posts found!</p>
  }

</div>

Listing 37. List.cshtml

Note that we have called the partial view _PostTemplate using the Html.Partial method, alternatively we can also use Html.RenderPartial.

Html.Partial and Html.RenderPartial

We can call a partial view using two ways, using Html.Partial or Html.RenderPartial. When we use the Html.RenderPartial the generated html is directly written to the output response which is efficient in large html cases.

Our view is pretty much completed. We have some pagination and routing work pending to complete this story.

4.7.4 Pagination

We are displaying only the latest 10 posts and not all. To see the older posts we need to implement pagination. Let's display the pagination links (next and previous) at both the top and bottom of the page, this is also a best case where we can go for a partial view.

Pagination Links

Pagination Links

Create a partial view _Pager.cshtml under the Shared folder. What we have to do in the pager partial view is display the previous and next pagination links based on the "total pages" and the "current page" values.

Here is the complete code and explaination follows the code.

@model JustBlog.Models.ListViewModel

@* Read the current page and total pages *@

@{
  var currentPage = ViewContext.RequestContext.HttpContext.Request["p"] != null
	? int.Parse(ViewContext.RequestContext.HttpContext.Request["p"])
	: 1;

  var totalPages = Math.Ceiling((double)Model.TotalPosts / 10);
}

@* Check do we need to display pagination links *@

@if (currentPage > 1 || currentPage < totalPages)
{
  var p = string.Format("p={0}", currentPage - 1);
  var n = string.Format("p={0}", currentPage + 1);

  @* If the view is rendered for "search" action append the pagination value with "&" *@

  if (ViewContext.RouteData.Values["action"].ToString()
	.Equals("search", StringComparison.OrdinalIgnoreCase))
  {
    var s = String.Format("?s={0}", ViewContext.RequestContext.HttpContext.Request.QueryString["s"]);
    p = String.Format("{0}&{1}", s, p);
    n = String.Format("{0}&{1}", s, n);
  }
  else
  {
    p = String.Concat("?", p);
    n = String.Concat("?", n);
  }

  <!--pagination links-->
  <div class="pager">

    <a href="@p" title="Previous" class="previous"
		style="visibility:@(currentPage > 1 ? "visible" : "collapse")"><< previous</a>

    <a href="@n" title="Next" class="next"
		style="visibility:@(currentPage < totalPages ? "visible" : "collapse")">next >></a>

  </div>
}

Listing 38. _Pager.cshtml

We have to know the total pages and the current page no. to show/hide the pagination links. The former one is directly available through the TotalPosts property of the ListViewModel and the later one can be read from query-string.

In the conditional block what we are doing is determining the next and previous page no. that will be specified as query-strings in the anchor links. In the search posts (we will be doing this story shortly) there will be already a query-string in the url and so we have to append our pagination value p with "&" which is taken care in the inner condition.

Update the List.cshtml to display the pagination links at both the top and bottom of the page.

@model JustBlog.Models.ListViewModel

<div id="content">

  <h1>@ViewBag.Title</h1>

  @Html.Partial("_Pager", Model)

  @if (Model.Posts.Count > 0)
  {
    foreach (var post in Model.Posts)
    {
      @Html.Partial("_PostTemplate", post)
    }
  }
  else
  {
    <p>No posts found!</p>
  }

  @Html.Partial("_Pager", Model)

</div>

Listing 39. List.cshtml

Our List view is completed. Before doing a test drive we have to fix the routes in the RouteConfig.cs under App_Start folder.

4.7.5 Fixing Routes

Routes are defined in the RouteConfig.cs file located in App_Start folder and they are used to map the incoming requests to controller actions. All the routes defined in an application are registered in the RouteTable. You will see an ignore route and map route definitions defined as default in the RegisterRoutes method. This RegisterRoutes method is called in the Application_Start event of Global.asax.cs.

using System.Web.Mvc;
using System.Web.Routing;

namespace JustBlog
{
    public static void RegisterRoutes(RouteCollection routes)
    {
	    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

	    routes.MapRoute(
		    "Default",
		    "{controller}/{action}/{id}",
		    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
	    );
    }
}

Listing 40. RouteConfig.cs

The IgnoreRoute extension method is used to ignore the requests (return 404) to be handled by routing infrastructure if they matches the pattern. In the above case, all the ".axd" requests are not handled by the routing module.

The MapRoute method is used to map an incoming request to a controller and action. The first parameter Default we have passed in the above definition is an optional name for the route. The second parameter is the pattern and the final one is the default values for the route parameters. If there is not a controller or an action segment available in the incoming request, they are taken from the default values.

Url pattern in routes

The pattern we specify while defining routes matches to the segments of the incoming url. The pattern approach helps us to avoid defining a separate route for each action.

For example, if the incoming request is "http://mysite.com/Admin/Index" and on matching this request with the default route ("{controller}/{action}/{id}") excluding the host name the first segment "Admin" is mapped to the controller, "Index" is mapped to the action and "id" is not mapped to anything (it is optional). So if there is a controller with name "AdminController" and if it has an action with name "Index" the request is forwarded to that action else 404 is returned.

We have to change the default controller and action names specified in the default route to Blog and Posts as below,

public static void RegisterRoutes(RouteCollection routes)
{
	routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

	routes.MapRoute(
		"Default",
		"{controller}/{action}/{id}",
		new { controller = "Blog", action = "Posts", id = UrlParameter.Optional }
	);
}

Listing 41. RouteConfig.cs

Before running the application we have to create a database with name JustBlog and specify the connection string in the web.config.

      <connectionStrings>
            <add name="JustBlogDbConnString"
                 connectionString="Data Source=SQLSERVER;Initial Catalog=JustBlog;Integrated Security=True"
                 providerName="System.Data.SqlClient"/>
      <cconnectionStrings>

Listing 42. Specifying connection string in web.config

You've to replace the Data Source property based upon your SQL Server configuration. The tables are created automatically by NHibernate when you run the application. You could download the script that inserts some dummy data to the tables from here.

One important thing you have to do is, comment out the below line in the RepositoryModule class in the JustBlog.Core project once the tables are created successfully. This helps to avoid recreating tables again in subsequent requests. You can also create tables manually by running this script.

ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(true, true, false))

Listing 43. RepositoryModule

If everything is perfect you'll see the below screen.

Test Drive

Test Drive

In our blog application we are not going to have much controllers. We have a BlogController and we will have one more called AdminController in the next part. So having two controllers in the application we can ignore the controller name specified in the url pattern. Instead of http://localhost/blog/posts we can simply use http://localhost/posts. To achieve this all we have to do is replace the default route defined in the RegisterRoutes method with the below route definition. Note that, the action links we have used in the views will generate the links correctly according to the changes in the routes. That's why I recommend always to use the built-in helper method to generate the links instead of hardcoding them directly in the href attribute.

routes.MapRoute(
	"Action",
	"{action}",
	new { controller = "Blog", action = "Posts" }
);

Listing 44. Default route

That's all! We have completed our first story. Now we can see the latest posts in our blog and navigate to the older posts as well.

5. Story #2 - Display posts based on category

Each post belongs to a category and in this story we are going to display the posts based on category.

To complete this story we have to complete the following tasks.

1. Create repository methods to fetch posts, total posts and category based on slug
2. Create an action that return posts belongs to a category
3. Define a new route in RouteConfig.cs file

5.1 Create repository methods to fetch posts, total posts and category based on slug

Let's define three new methods in the IBlogRepository interface.

public interface IBlogRepository
{
	...

	IList<Post> PostsForCategory(string categorySlug, int pageNo, int pageSize);
    int TotalPostsForCategory(string categorySlug);
	Category Category(string categorySlug);
}

Listing 45. IBlogRepository

PostsForCategory method returns the latest posts belongs to a category based on the slug (UrlSlug) and pagination values. The TotalPostsForCategory method returns the total no. of posts belongs to the category. The Category method returns the category instance.

Here is the implementation of the methods.

public IList<Post> PostsForCategory(string categorySlug, int pageNo, int pageSize)
{
  var posts = _session.Query<Post>()
                      .Where(p => p.Published && p.Category.UrlSlug.Equals(categorySlug))
                      .OrderByDescending(p => p.PostedOn)
                      .Skip(pageNo * pageSize)
                      .Take(pageSize)
                      .Fetch(p => p.Category)
                      .ToList();

  var postIds = posts.Select(p => p.Id).ToList();

  return _session.Query<Post>()
                .Where(p => postIds.Contains(p.Id))
                .OrderByDescending(p => p.PostedOn)
                .FetchMany(p => p.Tags)
                .ToList();
}

public int TotalPostsForCategory(string categorySlug)
{
	return _session.Query<Post>()
				.Where(p => p.Published && p.Category.UrlSlug.Equals(categorySlug))
				.Count();
}

public Category Category(string categorySlug)
{
	return _session.Query<Category>()
				.FirstOrDefault(t => t.UrlSlug.Equals(categorySlug));
}

Listing 46. BlogRepository

5.2 Create an action to return posts belongs a particular category

We need a new action in our BlogController to diplay the posts based on category. Create a new action with the name Category that takes both the slug parameter and pagination values.

public ViewResult Category(string category, int p = 1)
{
	// TODO: get the posts for the category and return the view.
}

Listing 47. Category action

We can use the same ListViewModel by adding an overloaded constructor that takes category slug as a parameter. Here is the modified ListViewModel. We also added a new property called Category to store the object.

public class ListViewModel
{
	public ListViewModel(IBlogRepository blogRepository, int p)
	{
		Posts = blogRepository.Posts(p - 1, 10);
		TotalPosts = blogRepository.TotalPosts();
	}


	public ListViewModel(IBlogRepository blogRepository,
		string categorySlug, int p)
	{
		Posts = blogRepository.PostsForCategory(categorySlug, p - 1, 10);
		TotalPosts = blogRepository.TotalPostsForCategory(categorySlug);
		Category = blogRepository.Category(categorySlug);
	}

	public IList<Post> Posts { get; private set; }
	public int TotalPosts { get; private set; }
	public Category Category { get; private set; }
}

Listing 48. ListViewModel

Our viewmodel is ready, let's complete the pending work in the action.

public ViewResult Category(string category, int p = 1)
{
	var viewModel = new ListViewModel(_blogRepository, category, p);

	if (viewModel.Category == null)
		throw new HttpException(404, "Category not found");

	ViewBag.Title = String.Format(@"Latest posts on category ""{0}""",
						viewModel.Category.Name);
	return View("List", viewModel);
}

Listing 49. Category action

Sometimes user can look for posts belongs to a category that not exists and in that case the Category property will be null. We are throwing a 404 exception if there is no category exists in database for the passed slug. We will see in Part III how to handle these exceptions at application level and display a custom error view to the user.

5.3 Define a new route in RouteConfig.cs file

We need to map requests like http://localhost/category/programming, http://localhost/category/unittesting to Category action. The default route we have currently not supports this rule. Let's define a new route above the default one that maps these requests to the Category action.

routes.MapRoute(
	"Category",
	"Category/{category}",
	new { controller = "Blog", action = "Category" }
);

Listing 50. New route for Category action

Route Order

The route that defined at first has the highest priority. If the incoming request not matches the first route then it is matched with the second, third and so on until it matches any of the defined route. If none of the route matches then 404 error will be returned.

Our second story is over, now we can browse posts based on category. In the next story we will see how to display posts labelled for a tag.

6. Story #3 - Display posts based on tag

This story is similar as the previous one and in this story we are going to display posts labelled for a particular tag.

The following are the tasks we are going to do.

1. Create repository methods to fetch posts, total posts and tag based on slug
2. Create an action to return posts for a particular tag
3. Define a new route in RouteConfig.cs file

6.1 Create repository methods to fetch posts, total posts and tag based on slug

We have to define three new methods in IBlogRepository.

public interface IBlogRepository
{
	...

	IList<Post> PostsForTag(string tagSlug, int pageNo, int pageSize);
	int TotalPostsForTag(string tagSlug);
	Tag Tag(string tagSlug);
}

Listing 51. IBlogRepository

Here is the implementation of those methods.

public IList<Post> PostsForTag(string tagSlug, int pageNo, int pageSize)
{
  var posts = _session.Query<Post>()
                    .Where(p => p.Published && p.Tags.Any(t => t.UrlSlug.Equals(tagSlug)))
                    .OrderByDescending(p => p.PostedOn)
                    .Skip(pageNo * pageSize)
                    .Take(pageSize)
                    .Fetch(p => p.Category)
                    .ToList();

  var postIds = posts.Select(p => p.Id).ToList();

  return _session.Query<Post>()
                .Where(p => postIds.Contains(p.Id))
                .OrderByDescending(p => p.PostedOn)
                .FetchMany(p => p.Tags)
                .ToList();
}

public int TotalPostsForTag(string tagSlug)
{
	return _session.Query<Post>()
				.Where(p => p.Published && p.Tags.Any(t => t.UrlSlug.Equals(tagSlug)))
				.Count();
}

public Tag Tag(string tagSlug)
{
	return _session.Query<Tag>()
				.FirstOrDefault(t => t.UrlSlug.Equals(tagSlug));
}

Listing 52. BlogRepository

6.2 Create an action to return posts for a particular tag

We have to create a new action Tag to display posts labelled for a tag.

public ViewResult Tag(string tag, int p = 1)
{
	// TODO: get the posts for the tag and return the view.
}

Listing 53. Tag action

We have to modify the overloaded constructor of the ListViewModel as below to use it in the Tag action.

public class ListViewModel
{
	public ListViewModel(IBlogRepository blogRepository, int p)
	{
		Posts = blogRepository.Posts(p - 1, 10);
		TotalPosts = blogRepository.TotalPosts();
	}


	public ListViewModel(IBlogRepository blogRepository,
		string text, string type, int p)
	{
		switch (type)
		{
			case "Tag":
				Posts = blogRepository.PostsForTag(text, p - 1, 10);
				TotalPosts = blogRepository.TotalPostsForTag(text);
				Tag = blogRepository.Tag(text);
				break;
			default:
				Posts = blogRepository.PostsForCategory(text, p - 1, 10);
				TotalPosts = blogRepository.TotalPostsForCategory(text);
				Category = blogRepository.Category(text);
			break;
		}
	}

	public IList<Post> Posts { get; private set; }
	public int TotalPosts { get; private set; }
	public Category Category { get; private set; }
	public Tag Tag { get; private set; }
}

Listing 54. ListViewModel

We have modified the constructor to pass additional parameter type that represents to fetch posts based on category or tag. We also added a new property Tag.

Here is the implementation of the Tag action. We are passing the type as "Tag" to fetch the posts tagged for the corresponsing tag.

public ViewResult Tag(string tag, int p = 1)
{
	var viewModel = new ListViewModel(_blogRepository, tag, "Tag", p);

	if (viewModel.Tag == null)
		throw new HttpException(404, "Tag not found");

	ViewBag.Title = String.Format(@"Latest posts tagged on ""{0}""",
		viewModel.Tag.Name);
	return View("List", viewModel);
}

Listing 55. Tag action

Since we have changed the constructor of the ListViewModel, we need to update the Category action to pass the type as "Category".

public ViewResult Category(string category, int p = 1)
{
	var viewModel = new ListViewModel(_blogRepository, category, "Category", p);

	if (viewModel.Category == null)
		throw new HttpException(404, "Category not found");

	ViewBag.Title = String.Format(@"Latest posts on category ""{0}""",
		viewModel.Category.Name);
	return View("List", viewModel);
}

Listing 56. Category action

6.3 Define a new route in RouteConfig.cs file

Like in previous story, we need to add another route to direct requests to Tag action.

routes.MapRoute(
	"Tag",
	"Tag/{tag}",
	new { controller = "Blog", action = "Tag" }
);

Listing 57. Tag route

We have completed this story as well. In the next story we will see how to search posts.

7. Story #4 - Search posts

In this story, we are going to implement the search functionality for our blog.

The following are the tasks we are going to execute as part of this story.

1. Create a partial view to display the search textbox
2. Create necessary repository methods
3. Create search action

7.1 Create a partial view to display the search textbox

Create a new partial view with name _Search.cshml under the Shared folder. We have to create a html form and that can be easily accomplished by the Html.BeginForm() html-helper.

@using (Html.BeginForm(
	"Search", 			// action
	"Blog", 			// controller
	FormMethod.Get, 	// method
	new { id = "search-form" })) // html attributes
{
	

@Html.TextBox("s")

}

Listing 58. Search form

Html.BeginForm

This is a special html-helper that renders a html form. The helper implements IDisposable to make sure that the form end tag is closed after rendering the child controls.

We want to display the search form in all the pages and so let's update the _Layout.cshtml to include the _Search.cshtml partial view above the RenderBody.

...
<div id="site_content">
	@Html.Partial("_Search")
	@RenderBody()
</div>
...

Listing 59. _Layout.cshtml

7.2 Create necessary repository methods

We have to create two methods, one method to fetch the posts that matches the search text and the other one is to display the total number of posts matches the search text for pagination.

public interface IBlogRepository
{
	...
	IList<Post> PostsForSearch(string search, int pageNo, int pageSize);
	int TotalPostsForSearch(string search);
}

Listing 60. IBlogRepository

Here is the implementation of both the methods.

public IList<Post> PostsForSearch(string search, int pageNo, int pageSize)
{
  var posts = _session.Query<Post>()
                        .Where(p => p.Published && (p.Title.Contains(search) || p.Category.Name.Equals(search) || p.Tags.Any(t => t.Name.Equals(search))))
                        .OrderByDescending(p => p.PostedOn)
                        .Skip(pageNo * pageSize)
                        .Take(pageSize)
                        .Fetch(p => p.Category)
                        .ToList();

  var postIds = posts.Select(p => p.Id).ToList();

  return _session.Query<Post>()
        .Where(p => postIds.Contains(p.Id))
        .OrderByDescending(p => p.PostedOn)
        .FetchMany(p => p.Tags)
        .ToList();
}

public int TotalPostsForSearch(string search)
{
	return _session.Query<Post>()
			.Where(p => p.Published && (p.Title.Contains(search) || p.Category.Name.Equals(search) || p.Tags.Any(t => t.Name.Equals(search))))
			.Count();
}

Listing 61. BlogRepository

If you see the implementation of PostsForSearch method, we are searching the posts that matches the text either in title, category or tags.

7.3 Create search action

We need an action that displays the posts matches the search text and it's pretty much the same as the Category and Tag actions.

public ViewResult Search(string s, int p = 1)
{
	ViewBag.Title = String.Format(@"Lists of posts found
						for search text ""{0}""", s);

	var viewModel = new ListViewModel(_blogRepository, s, "Search", p);
	return View("List", viewModel);
}

Listing 62. Search action

Let's update the ListViewModel.

public class ListViewModel
{
	public ListViewModel(IBlogRepository blogRepository, int p)
	{
		Posts = blogRepository.Posts(p - 1, 10);
		TotalPosts = blogRepository.TotalPosts();
	}


	public ListViewModel(IBlogRepository blogRepository,
		string text, string type, int p)
	{
		switch (type)
		{
			case "Category":
				Posts = blogRepository.PostsForCategory(text, p - 1, 10);
				TotalPosts = blogRepository.TotalPostsForCategory(text);
				Category = blogRepository.Category(text);
				break;
			case "Tag":
				Posts = blogRepository.PostsForTag(text, p - 1, 10);
				TotalPosts = blogRepository.TotalPostsForTag(text);
				Tag = blogRepository.Tag(text);
				break;
			default:
				Posts = blogRepository.PostsForSearch(text, p - 1, 10);
				TotalPosts = blogRepository.TotalPostsForSearch(text);
				Search = text;
				break;
		}
	}

	public IList<Post> Posts { get; private set; }
	public int TotalPosts { get; private set; }
	public Category Category { get; private set; }
	public Tag Tag { get; private set; }
	public string Search { get; private set; }
}

Listing 63. ListViewModel

We have added a new property Search in the ListViewModel to store the search text and display it in the view.

We don't want the form getting submitted on clicking the Search button without entering any text in the search-box and we can achieve that through a simple script. Create a folder with name Scripts. Add a script file app.js under the folder. All we have to do in the script file is listen to the submit event of the form and stop the event if there is no any text in the search-box.

$(function () {

	$('#search-form').submit(function () {
		if ($("#s").val().trim())
			return true;
		return false;
	});
});

Listing 64. app.js

Don't forget to include the script file app.js in the _Layout.cshtml page along with the jquery library.

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.2.min.js"></script>
<script src="@Url.Content("~/Scripts/app.js")"></script>

Listing 65. Referencing scripts in _Layout.cshtml

8. Story #5 - Display the details of a single post

The stories we have completed so far are about displaying collection of posts based upon category, tag or search text. In this story we will see how to display the details of a single post.

Following are the tasks we are going to execute as part of this story.

1. Create a repository method to return post based upon year, month and url-slug
2. Create an action to return the post view
3. Create view
4. Define a new route in RouteConfig.cs

8.1 Create a repository method to return post based upon year, month and url-slug

Each post is uniquely identified by the title slug (UrlSlug) along with the year and month the post is published. Define a new method in IBlogRepository that returns a post based upon those three parameters.

public interface IBlogRepository
{
	...

	Post Post(int year, int month, string titleSlug);
}

Listing 66. IBlogRepository

Implement the Post method in BlogRepository.

public Post Post(int year, int month, string titleSlug)
{
	var query = _session.Query<Post>()
						.Where(p => p.PostedOn.Year == year && p.PostedOn.Month == month && p.UrlSlug.Equals(titleSlug))
						.Fetch(p => p.Category);

	query.FetchMany(p => p.Tags).ToFuture();

	return query.ToFuture().Single();
}

Listing 67. BlogRepository

8.2 Create an action to return the post view

Create a new action with the name Post that takes year, month and title (UrlSlug)

public ViewResult Post(int year, int month, string title)
{
	var post = _blogRepository.Post(year, month, title);

	if (post == null)
		throw new HttpException(404, "Post not found");

	if (post.Published == false && User.Identity.IsAuthenticated == false)
        throw new HttpException(401, "The post is not published");

	return View(post);
}

Listing 68. Post action

If the post is not published and the user is not admin then we are throwing 401 exception. Admin needs to see the post eventhough it's not published.

8.3 Create view

We need a separate view for the Post action with the same name. We could have used the partial view _PostTemplate but there are some differences in the html and so I decided to keep things separate.

@model JustBlog.Core.Objects.Post

@{
  ViewBag.Title = Model.Title;
}

<div id="content">
  <div class="post">
    <div class="post-meta">
      <div class="row">

        <div class="post-title">
          <h1>@Html.PostLink(Model)</h1>
        </div>

      </div>
      <div class="row">

        <div class="post-category">
          <span>Category:</span> @Html.CategoryLink(Model.Category)
        </div>

        <div class="post-tags">
          <span>Tags:</span> @Helpers.Tags(Html, Model.Tags)
        </div>

        <div class="posted-date">
          @Model.PostedOn.ToConfigLocalTime()
        </div>

      </div>
    </div>

    <div class="post-body">
     @Html.Raw(Model.Description)
    </div>

  </div>
</div>

Listing 69. Post view

8.4 Define a new route in RouteConfig.cs

The url for a post looks like http://localhost/archive/2012/11/some_post. We have three variable segments (year, month and title) in the request. Let's define a new route to handle these requests.

routes.MapRoute(
	"Post",
	"Archive/{year}/{month}/{title}",
	new { controller = "Blog", action = "Post" }
);

Listing 70. Post route

MVC Tip - Route Constraints

Route Constraints are used to constraint a route matching a request.

Ex.
routes.MapRoute(
    "Post",
    "Archive/Post/{year}-{month}-{day}",
    new { controller = "Blog", action = "Post" },
    new { year = @"\d{4}", month = @"\d{2}", day = @"\d{2}" }
);

The anonymous object we have passed as last parameter contains the route constraints set for the parameters in the route. There are different types of constraints and the ones we have used above are regular expression constraints. If the parameters in the incoming request doesn't matches the regular expression they fails by 404.

In the upcoming stories, we will see how to display the sidebar widgets using a specific feature of ASP.NET MVC called Child action.

9. Story #6 - Display the post categories in a widget

We have to display a set of widgets as a sidebar in our blog. In this story we are going to create the categories widget that displays all the categories in the blog. In the following stories we will see how to build other widgets to display tags and latest posts.

The following are the tasks we have to execute to complete this story.

1. Create a repository method that returns all the categories
2. Create a view model
3. Create a child action
4. Create necessary partial views

9.1 Create a repository method that returns all the categories

Define and implement the method Categories that returns all the categories from database.

public interface IBlogRepository
{
	...
	IList<Category> Categories();
}

Listing 71. IBlogRepository

The implementation is quite simple,

public IList<Category> Categories()
{
	return _session.Query<Category>().OrderBy(p => p.Name).ToList();
}

Listing 72. BlogRepository

9.2 Create a view model

We are going to display the complete sidebar through a single action. We need a new single view model that wraps all the widget data. Let's create a new view model called WidgetViewModel under the Models folder.

using JustBlog.Core;
using JustBlog.Core.Objects;
using System.Collections.Generic;

namespace JustBlog.Models
{
    public class WidgetViewModel
    {
	    public WidgetViewModel(IBlogRepository blogRepository)
	    {
		    Categories = blogRepository.Categories();
	    }

	    public IList<Category> Categories { get; private set; }
    }
}

Listing 73. WidgetViewModel

Currently our WidgetViewModel contains only property Categories of type IList<Category>, soon we are going to add other widgets data as well.

9.3 Create a child action

A child action is an action that could be called from a view and they cannot be called directly from the browser. We can make a normal action as child action by marking it with the ChildActionOnly attribute.

[ChildActionOnly]
public PartialViewResult Sidebars()
{
	var widgetViewModel = new WidgetViewModel(_blogRepository);
	return PartialView("_Sidebars", widgetViewModel);
}

Listing 74. Sidebars child action

Since we are returning a partial view, the return type of the action is specified as PartialViewResult.

Child Actions

Child actions are special actions that can be only called from views. We can invoke a child action either using Html.Action or Html.RenderAction. It is imporatant to note that a separate MVC life cycle starts when we invoke a child action. We can also pass model instances to a child action.

9.4 Create necessary partial views

Create two partials _Sidebars and _Categories under the Shared folder. The _Sidebars calls the _Categories and the other partial views passing the appropriate model.

@model JustBlog.Models.WidgetViewModel

<div id="sidebars">
  @Html.Partial("_Categories", Model.Categories)
</div>

Listing 75. _Sidebars.cshtml

Here is the content of _Categories.cshtml. Note that, our _Categories.cshtml is strongly-typed with IList<Category>. All we doing in the below markup is iterating the model and rendering it as an unordered list.

@model IList<JustBlog.Core.Objects.Category>

<div class="sidebar">
  <h3>Categories</h3>

  @if (Model.Count > 0)
  {
    <ul>
      @foreach (var category in Model)
      {
        <li>@Html.CategoryLink(category)</li>
      }
    </ul>
  }
  else
  {
    <p>No categories found!</p>
  }
</div>

Listing 76. _Categories.cshtml

Update the _Layout.cshtml to call the child action Sidebars.

...
<div id="site_content">
	@Html.Partial("_Search")
	@RenderBody()

	@* Call the child action to render the sidebar *@
	@Html.Action("Sidebars")
</div>
...

Listing 77. _Layout.cshtml

If you run the application you could see the Categories widget at the sidebar.

Let's complete the other widgets.

10. Story #7 - Display the post tags in a widget

This story is quite same as the previous one.

1. Create a repository method that returns all the tags
2. Update the WidgetViewModel
3. Create a partial view to display the tags

10.1 Create a repository method that returns all the tags

public interface IBlogRepository
{
	...
	IList<Tag> Tags();
}

Listing 78. IBlogRepository

public IList<Tag> Tags()
{
	return _session.Query<Tag>().OrderBy(p => p.Name).ToList();
}

Listing 79. BlogRepository

10.2 Update the WidgetViewModel

Add a new property Tags to the WidgetViewModel to store the collection of tags.

public class WidgetViewModel
{
	public WidgetViewModel(IBlogRepository blogRepository)
	{
		Categories = blogRepository.Categories();
		Tags = blogRepository.Tags();
	}

	public IList<Category> Categories { get; private set; }
	public IList<Tag> Tags { get; private set; }
}

Listing 80. WidgetViewModel

10.3 Create a partial view to display the tags

Create a partial view _Tags.cshtml that is strongly-typed with IList<Tag>.

@model IList<JustBlog.Core.Objects.Tag>

<div class="tags sidebar">
  <h3>Tags</h3>

  @if (Model.Count > 0)
  {
    foreach (var tag in Model)
    {
      @Html.TagLink(tag)
    }
  }
  else
  {
    <p>No tags found!</p>
  }
</div>

Listing 81. _Tags.cshtml

We need to update the _Sidebars.cshtml to render the tags as shown below.

@model JustBlog.Models.WidgetViewModel

<div id="sidebars">
  @Html.Partial("_Categories", Model.Categories)
  @Html.Partial("_Tags", Model.Tags)
</div>

Listing 82. _Sidebars.cshtml

MVC Tip: Caching

Outputs of actions can be cached by applying the [OutputCache] filter attribute over an action. MVC supports Donut-hole caching through child actions. In Donut-hole caching only a portion of the page is cached.

11. Story #8 - Display the latest posts in a widget

We already have the repository method that returns the latest posts. Now all we have to do is create a partial view to displays the posts. Create a partial view _LatestPosts.cshtml with the below contents.

@model IList<JustBlog.Core.Objects.Post>

<div class="sidebar">
  <h3>Latest Posts</h3>

  @if (Model.Count > 0)
  {
    <ul>
      @foreach (var post in Model)
      {
        <li>@Html.PostLink(post)</li>
      }
    </ul>
  }
  else
  {
    <p>No posts found!</p>
  }
</div>

Listing 83. _LatestPosts.cshtml

Update the _Sidebars.cshtml.

@model JustBlog.Models.WidgetViewModel

<div id="sidebars">
  @Html.Partial("_Categories", Model.Categories)
  @Html.Partial("_Tags", Model.Tags)
  @Html.Partial("_LatestPosts", Model.LatestPosts)
</div>

Listing 84. _Sidebars.cshtml

12. Summary

Great! We have completed all the stories of Part 1. Let's summarize the things that we have completed in this part. We have created the domain entities, database classes and other components that are part of the model layer. We have configured Fluent NHibernate and NHibernate for database interactions and Ninject for dependency injection. We have completed the stories that displays posts based on category, tag. We implemented search functionality for our blog. We also completed the stories to display the details of a single post and the sidebar widgets.

In the next part, we are going to build an admin console to manage the posts, categories and tags. We are going to learn some interesting things like how to implement forms authentication, how to write unit tests for controllers and much more.. Don't miss it!

Your feedback and suggestions are very valuable to me, so please share a comment!

Download Source  Fork in Github

blog comments powered by Disqus