Subscribe to entries Subscribe to comments

The ASP.NET MVC Application I am currently working on is really coming to life. I am very happy with the combination of Microsoft’s MVC with open source frameworks Rhino Commons, Castle Active Record and Nhibernate.

My previous post on this subject describes how and why I was interested in using Rhino.Commons.

This aim of this post is to provide a little more detail about what my colleagues and I are doing with Rhino commons, the Repository model and MVC routing. It is not intended to be a tutorial. I am simply interested in receiving feedback on this approach, especially where it could be improved.

We’re making heavy use of open source for this project so I feel duty bound to make my personal discoveries public however pedestrian they may seem to the hardcore open source committers. I would also like to mention that the controller and view work described below was done by another developer and I don’t take any credit for it. The strongly typed view data is pretty cool. I needed to include it to describe the broader context in which I am developing the Model, service layer and ultimately a reusable API.

The concepts touched upon in this post are

  • ASP.NET MVC’s URL Routing, Controllers and View data.
  • How and why I achieved fewer dependencies between MVC and Rhino Commons.
  • Use of C# Interfaces and Generics for greater abstraction and code reuse.
  • Using NHibernate Detached Criteria with Rhino Commons’s Repository.

 

ASP.NET MVC’s URL Routing, Controllers and View data.

A high priority requirement of the web application I am building is friendly URLs (humanly readable & Search Engine Optimised SEO). This is one reason why I jumped on ASP.NET MVC in the first place, but for really memorable URLs I need to be able to reference an object by a natural key - ideally a person’s name, news story title, blog title etc.

Already I’m in hot water as this presents an age old problem of uniqueness at the db table level. This is easier to talk about with a concrete example; I want to navigate directly to a web page for the late great bass player Jaco Pastorius. The URL needs to be http://domain.com/Artist/jaco-pastorius. Not too much to ask. The solution is going to be a lot like the permalink URLs generate by this blog engine.

The URL routing looks like this


routes.Add(new Route("Artist/{routableName}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Artist",
        action = "Index", routableName = "" }),
});

The controller (without all the comments and exception handling) looks like this:


using System;
using System.Web.Mvc;

namespace Company.Project.Web.Controllers
{
    using Core.API;
    using Core.Model;

    public class ArtistController : Controller
    {
        public class ArtistViewData
        {
            public Artist ArtistInstance{get;set;}
            public string CssFile{get;set;}
        } 

        public void Index(string routableName)
        {
            Artist artist = ArtistService.FindOne(routableName);
            ArtistViewData viewData =
                new ArtistViewData{ ArtistInstance = artist, CssFile = GetCssFile() };
            RenderView("Index", viewData);
        }
    }
}

The ArtistViewData class encapsulates the Artist instance and the Css file I need to skin the view. I could have passed the Artist instance directly to the View, but life is never that simple.

The Index.aspx View for Artist looks like this

Index.aspx


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Artist/Artist.Master"
    AutoEventWireup="true" CodeBehind="Index.aspx.cs"
    Inherits="Company.Project.Web.Views.Artist.Index" %>

<asp:Content ID="head" ContentPlaceHolderID="head" runat="server">
    <link href="/Content/<%= ViewData.CssFile %>" rel="stylesheet" type="text/css" />
</asp:Content>

<asp:Content ID="main" ContentPlaceHolderID="main" runat="server">
    <p><%= ViewData.ArtistInstance.Html %></p>
</asp:Content>

Index.aspx.cs


using System.Web.Mvc;

namespace Company.Project.Web.Views.Artist
{
    using Controllers;
    public partial class Index : ViewPage<ArtistController.ArtistViewData>
    {
    }
}

The Artist.Master Page hosts a range of MVC view controls that also make use of the strongly typed ArtistViewData. The content is rendered by HtmlHelper

I won’t dwell on this, but it’s extremely useful. Here is a brief excerpt from the Master page:

Artist.Master


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Artist.Master.cs"
    Inherits="Company.Project.Web.Views.Artist.Artist" %>
.. html
<div id="masthead">
    <h1><%= Html.RenderUserControl("~/Views/Artist/Name.ascx") %></h1>
</div>
.. more html

Artist.Master.cs


using System.Web.Mvc;

namespace Company.Project.Web.Views.Artist
{
    public partial class Artist : ViewMasterPage
    {
    }
}

The Name.ascx MVC user control simply renders the Artist’s name

Name.ascx


<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Name.ascx.cs"
        Inherits="Company.Project.Web.Views.Artist.Name" %>
<%= ViewData.ArtistInstance.Name %>

Name.ascx.cs


using System.Web.Mvc;

namespace Company.Project.Web.Views.Artist
{
    using Controllers;

    public partial class Name : ViewUserControl<ArtistController.ArtistViewData>
    {
    }
}

There is a similar control for the Css skinning. It contains a lot more logic which isn’t relevant to post here.

The point is the strongly typed data in the view and view controls. It is inherited by all the controls on the page that are of the correct type.

How and why I achieved fewer dependencies between MVC and Rhino Commons.

Hopping back to the Controller for a second, I’d like to elaborate on this section


public void Index(string routableName)
{
    Artist artist = ArtistService.FindOne(routableName);
    ...
}

Earlier in the project (about a week ago) it looked like this:


public void Index(string routableName)
{
    Artist artist = Repository<Artist>.FindOne(Where.Artist.Name == routableName);
}

So why did I hide the Rhino Commons Repository call in a static ArtistService class?

Well, a few reasons.

  1. Logic in one place. I wanted to hide away the details of the FindOne for routable name. I’m not enforcing unique values for the Artist.RoutableName, but the requirements could change at any moment and I would have to update all instances of Repository.FindOne(Where.Artist.Name == routableName); The static ArtistService class is the natural place to do that logic once and only once. I’ll come back to this in the next section.

  2. Using more than one repository. The static Artist Service class might interact with other data repositories in addition to the Rhino.Commons Repository, AR & NHibernate - legacy databases, text files, web services etc. The Controllers would never need to know the details, all they care about it having an instance of an Artist to work with, they don’t care how or from whence it came.

  3. Fewer namespaces. My controllers do not depend on the Rhino.Commons namespace directly. This gives me greater flexibility should I be required to use a different persistence framework in the future. The Controllers are ignorant of the persistence mechanics, they only interact with the Service Layer. It is a type of facade pattern.

  4. The Artist Service is a public API for all operations relating to the Artist class. The internals of the Persistence and FindBy methods are hidden away from the consumer of the API. This is a deliberate, conscious effort to make a simple, consistent, easy to use application interface.

  5. Testing and Code Coverage. The NHQG is fantastic but I know I’m not going to use all the FindBy methods it generates. I’m doing TDD and can’t possibly achieve 100% code coverage of the NHQG generated output (not in the time I have available, and it would be a bit odd to cover generated code anyway), but I can aim for 100% code coverage of the Artist Service class.

  6. Keeps the POCOs clear of any static helper methods. It is convenient to think of the Artist Service class as the static methods that would previously have belonged to the Artist Class. This is mainly a personal preference.

  7. Fewer base classes. This is related to increased use of generics. For example, previously I would have implemented a Persistent Object Base class that contained an Id primary key property. All classes that needed to be persisted would have inherited from this class. Usually this ends up in a stack of base classes each adding a few more properties at each stage of the hierarchy. I prefer not to use base classes for this kind of commonality now, but instead compose the object from many Interfaces. For exampl, IIdentifiable - an interface having the Id property - being the contract by which all persistent objects must abide. I think fits better with C# not having multiple-inheritance. I still have sub-classes for closely related classes, just not big fat stacks of base classes. I noticed James Kovacs doing the IIdentifiable interface in one of his screen casts and I liked it a lot. Thanks Mr Kovacs! (This point will become more relevant towards the end of this post)

Isn’t this just wrapping the calls to Repository<T> one layer deeper?

Not always. The static Artist Service class may perform several functions in one service method call often using other class instances and collections. For example, adding a Performance to the Artist’s touring schedule would be the responsibility of the Artist Service class but it may also alter the Performances collection instances, Ticketing instances. The services can interact with each other in ways the plain classes probably should not (because to do so would introduce very strong dependencies deep down the model).

Use of C# Interfaces and Generics for greater abstraction and code reuse.

The Artist object instance is just one of many that need to be accessible by RoutableName in my model. This is a prime candidate for an Interface. I shall call it IRoutable.

Classes that implement IRoutable are guaranteed to have the RoutableName property. I can therefore create a static RoutableService class to manipulate or make use of the RoutableName property for any class that implements IRoutable.

The interface will simply be defined as


namespace Company.Project.Core.API
{
    /// <summary>
    /// Classes that implement IRoutable can be located in MVC URL
    /// Routing using the RoutableName member
    /// </summary>
    public interface IRoutable
    {
        /// <summary>
        /// Gets or sets the routable name.
        /// </summary>
        /// <value>The routable name.</value>
        string RoutableName { get; set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        string Name { get; set; }
    }
}

A class that implements the IRoutable interface (using Artist again):


using System;
using Castle.ActiveRecord;
using Castle.Components.Validator;
using Company.Project.Core.API;

namespace Company.Project.Core.Model
{
    /// <summary>
    /// Artist Class
    /// </summary>
    [Serializable]
    [ActiveRecord("Artist")]
    public class Artist : IRoutable
    {
        [PrimaryKey]
        public int Id { get; set; }

        [Property(ColumnType = "StringClob", SqlType = "NTEXT")]
        public string Summary { get; set; }

        [Property(ColumnType = "StringClob", SqlType = "NTEXT")]
        public string Html { get; set; }

        #region IRoutable Members

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        [Property(Length = 1024), ValidateNonEmpty("The Name may not be empty.")]
        public virtual string Name { get; set; }

        /// <summary>
        /// Gets or sets the routable name.
        /// </summary>
        /// <value>The routable name.</value>
        [Property(Length = 1024), ValidateNonEmpty("The RoutableName may not be empty.")]
        public virtual string RoutableName { get; set; }

        #endregion
    }
}

I am now in a position to use C# 2.0 Generics to get any object of T by its RoutableName where T is IRoutable. This is where I step off the NHQG train, but I will sill use Rhino Commons’s Repository with generics and NHibernate Criteria.

Using NHibernate Detached Criteria with Rhino Commons’s Repository

I had already wrapped the Repository.FindOne(Where.Artist.Name == routableName) call inside the ArtistService class method FindOne(string routableName).

What comes next is really important to my argument for hiding away the Repository call in the first place; I’m going to change the implementation to use generics, but the Controller will never know, nor should it care. The controllers are actually being written by another developer. I can do whatever I want beneath the ArtistService.FindOne(routableName) method and the controller developer’s work will not be distrupted in the slightest.

Strong typed


Artist artist = Repository<Artist>.FindOne(Where.Artist.Name.Eq(routableName));

Generic typed


public static T FindOne<T>(string routableName) where T : IRoutable
{
    DetachedCriteria criteria = DetachedCriteria.For(typeof(T));
        criteria.Add(Expression.Eq("RoutableName", routableName));
    return Repository<T>.FindOne(criteria);
}

But this still isn’t quite what I need to implement a safe routable name that returns one instance, without enforcing a unique natural key. I need to add an order by clause and return the first row. This is an acceptable solution for the scale and resources of the project considering how unlikely it is for many instances of the same type to have the same routable name.

Here is my current working version of a RoutableName class that contains static helper methods for working with objects that need to be identified by name. (There is another class called RoutableNameService that generates a URL safe Routable name from various string inputs, but it uses so many of my other internal Regex, Unicode and String methods it wouldn’t make any sense to post it here). You may also notice another interface IPublishable, this contains a boolean property amongst other publishing meta-data that indicates whether or not the instance is available for viewing on the public facing web site. It gets set in the content management system that accompanies the web site.


using System.Collections.Generic;
using NHibernate.Criterion;
using Rhino.Commons;

namespace Company.Project.Core.Internals
{
    using API;

    /// <summary>
    /// Internal static helper methods for working with routable names
    /// </summary>
    internal static class RoutableService
    {
        /// <summary>
        /// Finds the first instance of T having routableName.
        /// If more than one instance of the same type has the same routableName,
        /// the one with the highest Id is returned where 2>1 makes 2 higher than 1.
        /// (Select Top 1 .. Order by Id Descending)
        /// </summary>
        /// <typeparam name="T">The type of instance to return</typeparam>
        /// <param name="routableName">The routableName by which to find the instance</param>
        /// <returns>The instance</returns>
        internal static T FindFirst<T>(string routableName)
               where T : IRoutable, IPublishable
        {
            return FindFirst<T>(routableName, false);
        }

        /// <summary>
        /// Finds the first instance of T having routableName.
        /// If more than one instance of the same type has the same routableName,
        /// the one with the highest Id is returned where 2>1 makes 2 higher than 1.
        /// (Select Top 1 .. Order by Id Descending)
        /// </summary>
        /// <typeparam name="T">The type of instance to return</typeparam>
        /// <param name="routableName">The routableName by which to find the instance</param>
        /// <param name="includeUnpublished">Whether to include unpublished data
                  in the result(s)</param>
        /// <returns>The instance</returns>
        internal static T FindFirst<T>(string routableName, bool includeUnpublished)
            where T : IRoutable, IPublishable
        {
            DetachedCriteria criteria = DetachedCriteria.For(typeof(T));
            criteria.Add(Expression.Eq("RoutableName", routableName))
                .AddOrder(Order.Desc("Id"));

            if (!includeUnpublished)
            {
                criteria.Add(Expression.Eq("CmsPublished", true));
            }

            return Repository<T>.FindFirst(criteria);
        }

        /// <summary>
        /// Finds all instances of T having routableName.
        /// </summary>
        /// <typeparam name="T">The type of instance to return</typeparam>
        /// <param name="routableName">The routableName by which to find the instance</param>
        /// <returns>The instance</returns>
        internal static ICollection<T> FindAll<T>(string routableName)
            where T : IRoutable, IPublishable
        {
            return FindAll<T>(routableName, false);
        }

        /// <summary>
        /// Finds all instance of T having routableName.
        /// </summary>
        /// <typeparam name="T">The type of instance to return</typeparam>
        /// <param name="routableName">The routableName by which to find the instance</param>
        /// <param name="includeUnpublished">Whether to include unpublished data in the
                   result(s)</param>
        /// <returns>The instance</returns>
        internal static ICollection<T> FindAll<T>(string routableName, bool includeUnpublished)
            where T : IRoutable, IPublishable
        {
            DetachedCriteria criteria = DetachedCriteria.For(typeof(T));
            criteria.Add(Expression.Eq("RoutableName", routableName))
                .AddOrder(Order.Desc("Id"));

            if (!includeUnpublished)
            {
                criteria.Add(Expression.Eq("CmsPublished", true));
            }

            return Repository<T>.FindAll(criteria);
        }
    }
}

One thing I don’t like in the above code is the bare strings “Id”, “RoutableName” and “CmsPublished”. Resharper will detect these if the property names change though, so it’s not all bad.

Internal and Public

You may have noticed that class is marked internal. That is also deliberate because I do not want to expose it in the API I am designing. The interfaces, custom types and methods in the service layer are the only things I want to be visible in the public API. Consequently these will be the only classes I will be required to document in full.

The ArtistService class method to FinOne now looks like this,


/// <summary>
/// Finds the artist having routableName.
/// </summary>
/// <param name="routableName">The routableName by which to find the artist.</param>
/// <param name="includeUnpublished">Whether to include unpublished artists</param>
/// <returns>The artist instance, or null</returns>
public static Artist FindOne(String routableName, bool includeUnpublished)
{
    return Internals.RoutableService.FindFirst<Artist>(routableName, includeUnpublished);
}

I have now abstracted the find method twice! Once to hide the detailed implementation (the logic decision to return the one with the highest ID) and once again to hide the generics. Ultimately this results in less code with greater separation of concerns, honestly.

The internal service classes contain all the generics and work exclusively with the Interface types defined in my API.

The Interface types in my API are almost all behavioral.

IIdentifiable - has a single integer primary identifier Id
IRoutable - has a routable name
IPositionable - has a spatial dimension - can be positioned in a list of ordered objects
ISchedulable - has a temporal dimension - can be positioned in a time-line / calendar
IPublishable - can be published in the content management system
IEditableText - has html blocks that can be edited in a WYSIWYG editor.

Core Interfaces

The Content Management System for this project is being developed to work with these interfaces.

There are regular ASP.NET User Controls that implement the same interfaces to capture user input.

Generic, static service helpers take objects of T and copy properties back & forth between instances of the classes and User Controls of the same interface type T. The amount of code to capture form data is as minimalist as I can get. I might write this up as a future post when I’ve completed the technique.

To conclude, Rhino.Commons, AR & NHibernate was the right decision for my project, but I’ve made a conscious effort to shield the MVC web site application from having any knowledge of their existence. This was done to achieve greater separation of layers and fewer direct dependencies.

kick it on DotNetKicks.com
10 Responses to “System.Web.MVC, Rhino Commons, Castle Active Record, Nhibernate, Part 2”
  1. Anthony says:

    Just wanted to say that I found your posts on this subject very interesting and highly educational.

    Of course it’s left me wanting more and I’ve got a tonne of questions and things I’d like to try.

    Would you proffer any advice on using transactions, say in the service layer perhaps - decorating the service calls with a [Transactional] attribute?

    Thanks

  2. mhanney says:

    Thank you for your feedback Anthony. It is very much appreciated.

    I have not used transactions at the service layer level, but to do so makes perfect sense. Thank you for suggesting this.

    As you are no doubt aware, some methods in the service classes contain a sequences of Repository calls that must complete as a single operation, otherwise the persistent data might be incomplete in the database.

    This is where my familiarity with Rhino.Commons is vague.

    I am not sure but I think Rhino.Commons.RhinoTransaction would be used something like this:

    public static void SaveOrUpdate(T instance) where T : IIdentifiable
    {
    RhinoTransaction tx = UnitOfWork.Current.BeginTransaction(IsolationLevel.ReadCommitted); // same as default

    .. do a series of Repository calls to change data

    try
    {
    IdentifiableService.SaveOrUpdate(instance);
    }
    catch
    {
    tx.Rollback();
    return;
    }

    tx.Commit();

    }

    Comments welcome.

    A [Transactional] attribute is another great idea.

  3. Beginning System.Web.MVC with Rhino Tools, Castle Active Record and Nhibernate | Michael Hanney says:

    […] « System.Web.MVC, Rhino Commons, Castle Active Record, Nhibernate, Part 2 Jun 02 2008 […]

  4. mhanney says:

    Update:

    With.Transaction(() => IdentifiableService.Save(instance));

    Appears to be the correct way to use transactions in Rhino.Commons

  5. ASP.NET MVC Archived Buzz, Page 1 says:

    […] [Del.icio.us] System.Web.MVC, Rhino Commons, Castle Active Record, Nhibernate, Part 2 | Michael Hann…Tuesday, July 08, 2008 from tobinharrisInteresting post about how someone is using NHibernate, ASP.NET MVC and Rhino Commons. Also, it’s cool to see how he’s structured his key interfaces to the application. […]

  6. Simone Busoli says:

    About transactions, Ayende suggests using the automatic transaction facility, since With.Transaction seems to be heavyweight. I’ve used it with no much success in an ASP.NET MVC application because it entails having controllers proxied, which leads to issues if you’re using custom attributes on actions parameters like those provided by mvccontrib.

  7. mhanney says:

    Hi Simone, thank you for your feedback, that is very good to know.

    I would like to correct something I said earlier then - I said, “With.Transaction() ‘Appears to be the correct way to use transactions in Rhino.Commons’”, but it is not the ‘correct’ way. It is just one way, and not recommended.

  8. Simone Busoli says:

    Besides, transactions is something I don’t think I need so often, and since with the Rhino stuff the session is flushed only when a transaction is committed, I’ve just added an extension method to the IRepository interface called Flush() which flushes the current session on demand. What it does is simply call UnitOfWork.Current.Flush().

  9. So you want to learn NHibernate? - Part 1 of 1, The Links « HSI Developer Blog says:

    […] System.Web.MVC with Rhino Tools, Castle Active Record and Nhibernate, Pt 2, Pt3RhinoCommons, NHibernate and ASP.NET MVC Part 1, Pt2, Pt3, Pt4, Pt5NHibernate and the Unit of […]

  10. So you want to learn NHibernate? (or, NHibernate Hyperlink Acupuncture) | The Freak Parade says:

    […] System.Web.MVC with Rhino Tools, Castle Active Record and Nhibernate, Pt 2, Pt3 RhinoCommons, NHibernate and ASP.NET MVC Part 1, Pt2, Pt3, Pt4, Pt5 NHibernate and the Unit of […]

Leave a Reply