Mapping a Twitter like domain with Fluent NHibernate

Sunday, 24 May 2009 10:00 by bengtbe

I'm currently learning NHibernate, and one of the best ways to learn is to blog about it :) Since I'm not a big fan of XML files, I also wanted to use Fluent NHibernate to do the mapping. As an example I will use a social messaging domain, similar to Twitter. I will also use a Top-down approach, starting with the domain model, writing the mapping, and finally creating the database schema using the SchemaExport class in NHibernate.

The domain model

Let's start with a look at the domain model:

 

The domain model mainly consists of the User entity and the Message entity and their associations:

  • The user has a list of Messages that he/she has posted.
  • The user has a list of other users that are interested in his/hers messages, called Followers.
  • The user has a list of other users whose messages he/she is interested, called Following.
  • The message has a PostedBy reference to the user who posted it.

Both entities derive from the Entity<T> base class that contains the Id property and implements Equals and GetHashCode methods.

The domain classes

Let's take a close look at the domain classes. Below is the class for the Message entity:

public class Message : Entity<Message>

{

    public virtual string Text { get; set; }

    public virtual User PostedBy { get; set; }

    public virtual DateTime CreatedAt { get; set; }

}

It contains the message text, a reference to the user who posted it, and the time it was created.

Below is the class for the User entity:

public class User : Entity<User>

{

    public User()

    {

        Name = Url = Email = "";

        Followers = new List<User>();

        Following = new List<User>();

        Messages = new List<Message>();

    }

 

    public virtual string Name { get; set; }

    public virtual string Username { get; set; }

    public virtual string Email { get; set; }

    public virtual string Url { get; set; }

    public virtual IList<User> Followers { get; private set; }

    public virtual IList<User> Following { get; private set; }

    public virtual IList<Message> Messages { get; private set; }

 

    public virtual void AddFollowing(User user)

    {

        if (Following.Contains(user)) return;

 

        Following.Add(user);

        user.Followers.Add(this);

    }

 

    public virtual void AddMessage(string messageText)

    {

        var message = new Message

                          {

                              Text = messageText,

                              PostedBy = this,

                              CreatedAt = DateTime.Now

                          };

        Messages.Add(message);

    }

}

The User class contains some properties about the user, a list of followers, a list of following users, and a list of messages. It also contains two methods:

  • AddFollowing(User user) - Adds another user to the list of users that the current user is following. Since this is a bidirectional relationship, it also adds the current user to the other user's followers.
  • AddMessage(string messageText) - Adds a new message that the user has posted. Sets the PostedBy to the current user.

Why is everything virtual?

When using NHibernate the domain model can be close to Persistence Ignorant (PI), which means that you don't have to take any special considerations when designing the domain model, like derive from a specific base class. However to support transparent lazy-loading, NHibernate needs to return a proxy that inherit from your entity and override the properties.

If you dislike marking everything with virtual you can turn of lazy-loading, but I don't recommend doing this.

Mapping with Fluent NHibernate

As mentioned, Fluent NHibernate lets you write mapping with strongly typed C# code, instead of the traditional NHibernate XML mapping files. This allows you to use tools like ReSharper to alter both the domain and mapping files when refactoring.

The mapping files must derive from ClassMap<T> and the mapping code is done inside the constructor using lambda expressions and a fluent syntax.

Let's take a look at the mapping file for the Message entity:

public class MessageClassMap : ClassMap<Message>

{

    public MessageClassMap()

    {

        Id(m => m.Id).GeneratedBy.GuidComb();

 

        Map(m => m.Text).Not.Nullable().WithLengthOf(140);

        Map(m => m.CreatedAt).Not.Nullable();

 

        References(m => m.PostedBy, "PostedBy").Not.Nullable();

    }

}

Explanation of the mapping:

  • The Id method maps the Id property as the identifier of the Message entity. It should be generated using the GuidComb algorithm.
  • The Map method maps the Text property. It also specifies that is should not be null, and that the max length is 120.
  • The Map method also maps the CreatedAt property.
  • The References method maps the PostedBy property as a many-to-one relationship between the message and user. Hence, one message belongs to a single user, but a user can have many messages. It also specifies that the column name in the database should be PostedBy.

Let's take a look at the mapping file for the User entity:

public class UserClassMap : ClassMap<User>

{

    public UserClassMap()

    {

        Id(u => u.Id);

        Map(u => u.Name).Not.Nullable().WithLengthOf(50);

        Map(u => u.Username).Not.Nullable().WithLengthOf(50).Unique();

 

        Map(u => u.Email).Not.Nullable().WithLengthOf(100);

        Map(u => u.Url).Not.Nullable().WithLengthOf(100);

 

        HasMany(u => u.Messages).LazyLoad().Cascade.All()

            .KeyColumnNames.Add("PostedBy");

 

        HasManyToMany(u => u.Following).LazyLoad()

            .WithParentKeyColumn("FollowerId")

            .WithChildKeyColumn("FollowingId");

 

        HasManyToMany(u => u.Followers).LazyLoad()

            .WithParentKeyColumn("FollowingId")

            .WithChildKeyColumn("FollowerId").Inverse();

    }

Explanation of the mapping:

  • The mapping of the Id, Name, Email, Url does not contain any new syntax.
  • The mapping of the UserName property uses the Unique method to specify uniqueness. This will create a unique constrain in the database schema.
  • The HasMany method maps the Messages property as a one-to-many between the user entity and message entity. The Cascade.All means the message will become persistent when you add it to a persistent user. The messages will also be deleted when you delete the user. It also specifies that the name of the column should be PostedBy in order to make sure that it uses the same column as specified in the message mapping. 
  • The HasManyToMany method maps the Following as a many-to-many mapping between users with specified column names.
  • The HasManyToMany method maps the Followers as a many-to-many mapping between users. This is the inverse of the Following association, hence the call to Inverse().

We also call LazyLoad() to specify that we want support for lazy loading of the lists.

Is something missing in the mapping?

You might have noticed that we often don't specify the table or column name in the mapping. In these cases the table will get the same name as the class, and the column will get the same name as the property. Below is an example of how to specify table and column names:

WithTable("TBL_MESSAGES");

Map(c => c.CreatedAt, "COL_CREATEDAT").Not.Nullable();

You also don't need to specify the type of the properties (e.g. that PostedBy is of type User). NHibernate uses reflection to determine the type.

Creating the database

In a top-down approach you change the database after you make changes to the domain or the mapping. During development you can recreate the database using the SchemaExport class in a test or console application. Below is an example using an explicit test in NUnit: 

    [TestFixture]

    public class DatabaseSetup

    {

        [Test, Explicit]

        public void SetupDatabase()

        {

            FluentConfiguration conf = NHConfiguration.CreateConfiguration(false);

            conf.ExposeConfiguration(BuildSchema)

                .BuildSessionFactory();

        }

 

        private static void BuildSchema(Configuration conf)

        {

            new SchemaExport(conf).Drop(false, true);

            new SchemaExport(conf).Create(false, true);

        }

    }

}

The test is marked with the Explicit attribute because you want to control when this test is run, e.g. you do not want to run it every time you run your tests. The SetupDatabase() test receives a FluentConfiguration from the NHibernate configuration class in the project. The database is created in the BuildSchema method that is passed to the ExposeConfiguration method. The call to BuildSessionFactory() is needed to trigger the FluentConfiguration to actually execute the call to the BuildSchema method.

Finally the database schema

Below is the database schema that has been created from the domain model and the mappings:

 

Some notes about the database schema:

  • The length of the nvarchar fields are according to the WithLengthOf call in the mappings.
  • All fields are not nullable because of the Not.Nullable() call in the mappings.
  • The many-to-many association between users are represented using the UserToUser junction table.
  • The one-to-many association between the message and users are done by the PostedBy column in the Message table.
  • It is not shown in the diagram, but Username has a unique constrain due to the Unique() call in the mappings.

Conclusion

In this post I have shown how to use Fluent NHibernate in a top-down approach in a Twitter like domain. Since I'm fairly new to NHibernate I welcome any comments and suggestions!

kick it on DotNetKicks.com   Shout it


Eliminating ToList() calls by TDD and Extensions Methods

Friday, 6 March 2009 00:34 by bengtbe

One important principle of reusable object oriented design is "Program to an interface, not an implementation". If you want to read more about the use of interfaces check out the great blog post Back to Basics: Interfaces by Karl Seguin. To summarize, instead of doing this:

interface IUserService

{

   List<User> GetUsers();

}

You should do something like this:

interface IUserService

{

   IList<User> GetUsers();

}

The I in front of List makes the big difference :) However, if you often use the delegate methods of the List class then calls to ToList() will start to pop-up all around the code, e.g.:

users.ToList().ForEach(user => oldestAge = Math.Max(oldestAge, user.Age));

I would rather want this syntax:

users.ForEach(user => oldestAge = Math.Max(oldestAge, user.Age));

We can add this syntax using Extension methods that was introduces in C# 3.0. 

Extension Methods

Extension methods enable you to "add" methods to existing classes and interfaces. They are static methods, but they are called as instance methods on the extended class or interface. The ForEach extension method could be added to the IList interface, however this would limit it to collections that implement this interface. A better match for this method is the generic IEnumerable interface. Then it can be used with virtually any of .NET's collections.

We are going to use Test Driven Development to write the extension method, following the mantra of Red, Green, Refactor.

Red - Create a failing test

Red means writing and running a test that fails. Don't skip this step! It is important to rule out bugs in the test. If your test passes before you write the code to satisfy the test, then it's doesn't mean much if it also passes after you write the code. Let's write the test:

[TestFixture]

public class EnumerableExtensionsTest

{

    [Test]

    public void Generic_IEnumerable_should_have_ForEach_extension()

    {

        IEnumerable<int> listOfInts = new List<int> {2, 4, 6};

 

        int result = 0;

        listOfInts.ForEach(x => result += x);

 

        Assert.That(result, Is.EqualTo(12));

    }

}

This test uses NUnit, however the syntax is very similar for both MsTest and MbUnit. It testes the ForEach method by using it to calculate the sum of a collection of integers. This method will of course not compile, since IEnumerable<int> does not have a ForEach method. Let's make it compile:

public static class EnumerableExtensions

{

    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)

    {

        return;

    }

}

This is an extension method. It must satisfy three conditions:

  1. The class must be static
  2. The method must be static
  3. The first argument of the method starts with this, indicating that the extension method belongs to this parameter.

Hence the first argument enumerable is the collection that the extension method is called upon. The second argument action is a delegate that encapsulates a method that take a single parameter and does not return a value. In the test method this delegate encapsulated x => result += x.

The code will now compile. Let's run the test:

 NUnit.Framework.AssertionException:   Expected: 12
  But was:  0

As you can see the test failed. This finishes the Red part of TDD.

Green - Make the test pass

Now that we are sure that we have a failing test, we should make it pass:

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)

{

    foreach (var item in enumerable)

    {

        action.Invoke(item);

    }

}

This is all the code that is needed. It iterates over the collection, invoking the action with the current item as the argument.

The test will now pass. This finishes the Green part of TDD. 

Refactor - Change the code

The last step of TDD is Refactor. The extension method is fairly simple, so there isn't much to change there. So the refactoring in this case involves moving the class to a suitable place in the solution, updating the namespace and so forth. After these changes you should run the test again to make sure the code still works.

Another example - Convert All

I'm going to finish of this post by quickly showing you another example: How to create a ConvertAll extension method on the IEnumerable interface. First let's write the test:

[Test]

public void Generic_IEnumerable_should_have_ConvertAll_extension()

{

    IEnumerable<int> listOfInts = new List<int>() { 2, 4, 6 };

 

    IList<string> listOfStrings = listOfInts.ConvertAll(x => "T" + x).ToList();

 

    Assert.That(listOfStrings.Count, Is.EqualTo(3));

    Assert.That(listOfStrings[0], Is.EqualTo("T2"));

    Assert.That(listOfStrings[1], Is.EqualTo("T4"));

    Assert.That(listOfStrings[2], Is.EqualTo("T6"));

}

The code to satisfy this test is as follows: 

public static IEnumerable<TOut> ConvertAll<T,TOut>(this IEnumerable<T> enumerable, Func<T,TOut> func)

{

    foreach (var item in enumerable)

    {

        yield return func.Invoke(item);

    }

}


I hope you found this post interesting! 

kick it on DotNetKicks.com   Shout it