This is the first post in a series of three where I'm going to see how we can change the NerdDinner project to use Fluent NHibernate instead of LINQ to SQL:
Introduction
NerdDinner.com is a site where you can organize dinner meetings for nerds, but even more, it is a great tutorial to the ASP.NET MVC framework. If you want to learn this framework (you should!) download the free tutorial (186 pages) from the site and get going!
NerdDinner uses LINQ to SQL as a data access layer generator. LINQ to SQL is perfectly suitable for this project, since the domain is small, and not very complex. However in these posts I would like to explore how the project would look using a real Object/Relational Mapper (O/RM). More specifically, I'm going to use Fluent NHibernate.
Preperations
First I downloaded the latest source code from CodePlex using the Subversion client TortoiseSVN. Then I added references to the NHibernate, Fluent NHibernate, and LINQ to NHibernate assemblies.
The existing domain model
Most of the domain model in NerdDinner is auto generated from the database as partial classes by the LINQ to SQL designer. You can find the Dinner and RSVP class in the NerdDinner.Designer.cs file; however since they are auto generated they are not very readable.
The Dinner class is also extended (using partial classes) with additional methods in the Dinner.cs file.
The new domain model
Since most of the existing domain model is auto generated we need to write a new domain model. The new Dinner class looks as follows:
public class Dinner
{
public Dinner()
{
RSVPs = new List<RSVP>();
}
public virtual int DinnerID { get; private set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual DateTime EventDate { get; set; }
public virtual double Latitude { get; set; }
public virtual double Longitude { get; set; }
public virtual string Country { get; set; }
public virtual string Address { get; set; }
public virtual string HostedBy { get; set; }
public virtual string ContactPhone { get; set; }
public virtual IList<RSVP> RSVPs { get; private set;}
public virtual void AddRSVP(string attendeeName)
{
if (dinner.IsUserRegistered(User.Identity.Name)) return;
RSVPs.Add(new RSVP
{
AttendeeName = attendeeName,
Dinner = this
});
}
public virtual bool IsHostedBy(string userName) { ... }
public virtual bool IsUserRegistered(string userName) { ... }
public virtual bool IsValid { ... }
public virtual IEnumerable<RuleViolation> GetRuleViolations() { ... }
}
The new RSVP class looks as follows:
public class RSVP
{
public virtual int RsvpID { get; private set; }
public virtual Dinner Dinner { get; set; }
public virtual string AttendeeName { get; set; }
}
If you wonder why every method is marked by virtual, this is a
requirement of NHibernate in order to support Lazy-Loading. I explained
this in more detail in the post Mapping a Twitter like domain with Fluent NHibernate.
The public interface of the domain model is more or less identical to the existing domain model. However I did make some changes.
Protect thy privates!
One of the changes I made was to make some of the property setters private; DinnerID, RSVPs, and RsvpID. Since the consumer code (the controllers) is not supposed to change to these properties we want to communicate this to our fellow programmers. The next change is also about protecting privates.
New method to add a RSVP to a dinner
To the Dinner class I added a new method called AddRSVP. By using terms from Domain Driven Design, the Dinner entity is the aggregate root of the RSVP entity. This basically means that a RSVP must belong to a Dinner; hence I want the Dinner class to control the list of RSVPs.
This change will help reduce code in the controllers and remove duplication. Previously the creating and adding of RSVPs was done both in the RSVPController.Register and the DinnerController.Create action methods. The Register method is shown below:
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsUserRegistered(User.Identity.Name)) {
RSVP rsvp = new RSVP();
rsvp.AttendeeName = User.Identity.Name;
dinner.RSVPs.Add(rsvp);
dinnerRepository.Save();
}
return Content("Thanks - we'll see you there!");
}
Thanks to our new method, this can now be reduced to:
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
dinner.AddRSVP(User.Identity.Name);
return Content("Thanks - we'll see you there!");
}
Similar reduction can be done to the Create method. BTW if you wonder what happened to the call to the Save method then we will come back to this in part 3 (teaser!).
As a rule of thumb always try to keep your action methods as simple as possible!
More object-oriented
The last change is the removal of the DinnerId property from the RSVP class. This property is purly database related, and does not belong in an object-oriented domain model. You can use the Dinner propety to get the DinnerId.
finally{}
In my opinion one of the benefits of using an OR/M like NHibernate as supposed to LINQ to SQL is that the domain model is much clearer. When trying to understand a new project one of the best places to start is by looking at the domain. I also did some changes (improvements?) to the domain model to better communicate intent, and to reduce code duplication.
The next step is to tell NHibernate how to map this domain model to the database. This will be explained in the next post where we look at the mapping.
If you liked this post then please shout and kick me :)