NerdDinner with Fluent NHibernate Part 3 - The infrastructure

Thursday, 8 October 2009 05:00 by bengtbe

This is the final 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

In the first post we took a look at the domain model of NerdDinner.com, and recreated and improved the code that was auto generated by the LINQ to SQL designer. In the second post we saw how to map this domain model to the database using Fluent NHibernate. In this final post we are going to take a look at the required changes to the rest of the application.

Session per request

In order for the repository to use NHibernate it needs to get a hold of an ISession. In this application I have chosen to follow the session per request pattern, in which a session is created at the beginning of the HTTP request, and closed at the end of the HTTP request. I also wanted to keep the changes to the NerdDinner project as small as possible; hence the entire infrastructure for NHibernate is isolated in a single class:

public class NHibernateSessionPerRequest : IHttpModule

{

    private static readonly ISessionFactory _sessionFactory;

 

    static NHibernateSessionPerRequest()

    {

        _sessionFactory = CreateSessionFactory();

    }

 

    public void Init(HttpApplication context)

    {

        context.BeginRequest += BeginRequest;

        context.EndRequest += EndRequest;

    }

 

    public static ISession GetCurrentSession()

    {

        return _sessionFactory.GetCurrentSession();

    }

 

    public void Dispose() { }

 

    private static void BeginRequest(object sender, EventArgs e)

    {

        ISession session = _sessionFactory.OpenSession();

        session.BeginTransaction();

        CurrentSessionContext.Bind(session);

    }

 

    private static void EndRequest(object sender, EventArgs e)

    {

        ISession session = CurrentSessionContext.Unbind(_sessionFactory);

 

        if (session == null) return;

 

        try

        {

            session.Transaction.Commit();

        }

        catch (Exception)

        {

            session.Transaction.Rollback();

        }

        finally

        {

            session.Close();

            session.Dispose();

        }

    }

 

    private static ISessionFactory CreateSessionFactory()

    {

        string connString = "NerdDinnerConnectionString";

 

        FluentConfiguration configuration = Fluently.Configure()

        .Database(MsSqlConfiguration.MsSql2005.ConnectionString(

           x => x.FromConnectionStringWithKey(connString)))

        .ExposeConfiguration(

            c => c.SetProperty("current_session_context_class", "web"))

        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Dinner>());

 

        return configuration.BuildSessionFactory();

    }

}


Some comments about the infrastructure:

  • It implements the IHttpModule, in order to plug into the request handling pipeline.
  • Since it is expensive to create an ISessionFactory you only want to create this once. This is why the factory is stored in a static field that is initialized by the static constructor.
  • At the beginning of the HTTP request the ISession is created and "stored" using the CurrentSessionContext.Bind method.
  • Other code can get a hold of this session by calling the GetCurrentSession method.
  • At the end of the HTTP request we "remove" the session by using the CurrentSessionContext.Unbind method. We also commit any changes, and close the session.

There are some drawbacks to this approach. We create a session for every request, even if it is not needed. However, a session is very lightweight, and we can easily tweak this behavior later. Another drawback is that we commit the transaction at the end of the HTTP request. In a more complex business application you probably want more control over your unit of work.

In order to activate this IHttpModule we need to add the following to httpModules/modules section of the Web.config:

<add name="NHibernateSessionPerRequest" type="NerdDinner.Models.NHibernateSessionPerRequest" />


Changes to the repository 

In the DinnerRepository we are mainly going to use LINQ to NHibernate to replace the LINQ to SQL queries:

public class DinnerRepository : IDinnerRepository

{

    public ISession Session

    {

        get { return NHibernateSessionPerRequest.GetCurrentSession(); }

    }

 

    public IQueryable<Dinner> FindAllDinners()

    {

        return Session.Linq<Dinner>();

    }

 

    public IQueryable<Dinner> FindUpcomingDinners()

    {

        return from dinner in FindAllDinners()

               where dinner.EventDate >= DateTime.Now

               orderby dinner.EventDate

               select dinner;

    }

 

    public IQueryable<Dinner> FindByLocation(float latitude, float longitude)

    {

        return Session.CreateSQLQuery(

                @"SELECT d.*

                    FROM Dinners d

                    JOIN NearestDinners(:Latitude, :Longitude) n

                      ON d.DinnerId = n.DinnerId

                   WHERE EventDate >= :EventDate

                   ORDER BY EventDate Desc")

            .AddEntity(typeof(Dinner))

            .SetDouble("Latitude", latitude)

            .SetDouble("Longitude", longitude)

            .SetDateTime("EventDate", DateTime.Now)

            .List<Dinner>().AsQueryable();

    }

   

    public Dinner GetDinner(int id)

    {

        return Session.Linq<Dinner>()

            .SingleOrDefault(d => d.DinnerID == id);

    }

 

    public void Add(Dinner dinner)

    {

        Session.SaveOrUpdate(dinner);

    }

 

    public void Delete(Dinner dinner)

    {

        Session.Delete(dinner);

    }

}


The LINQ to SQL and NHibernate Linq syntax are similar, so the changes to this class were fairly simple. However in the FindByLocation method I had to create a custom SQL query, because I don't think (not sure) that LINQ to NHibernate supports calling the database function NearestDinners.

Update: It seems that NHibernate Linq supports database functions. However I couldn't find much information about the functionality except for one simple example in the source code.

The ISession is retrieved by calling the GetCurrentSession() method. In an application with an IoC container I would prefer to inject the session in the constructor.

finally {}

In the final post in this series we have taken a look at the necessary changes to the infrastructure. We used a session per request pattern to create the session, and LINQ to NHibernate in the repository. As you hopefully have seen it was not very difficult to replace LINQ to SQL with Fluent NHibernate. As a benefit the domain part of the application has been greatly improved over the auto generated code from the LINQ to SQL designer.

If you liked this post then please shout and kick me :)

kick it on DotNetKicks.com

Shout it

NerdDinner with Fluent NHibernate Part 2 - The mapping

Tuesday, 25 August 2009 06:00 by bengtbe

This is the second 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

In the first post we took a look at the domain model of NerdDinner.com, and recreated and improved the code that was auto generated by the LINQ to SQL designer. In this post we are going to look at how to map this domain model to the database using Fluent NHibernate.

Fluent NHibernate

Fluent NHibernate gives you an alternative to NHibernate's XML mapping files. It lets you write mapping in strongly typed (and ReSharper friendly) C# code. In order to map an entity, you create a mapping class that derives from ClassMap<T>. All the mapping code is done inside the constructor by calling methods from the base class.

Last week the Release Candidate for Fluent NHibernate 1.0 was released. The code in this post has been updated with the changes in this release.

The mapping for the Dinner entity

The code for DinnerClassMap is shown below:

public class DinnerClassMap : ClassMap<Dinner>

{       

    public DinnerClassMap()

    {

        Table("Dinners");  

        Id(d => d.DinnerID).GeneratedBy.Identity();

        Map(d => d.Title).Not.Nullable().Length(50);

        Map(d => d.EventDate).Not.Nullable();

        Map(d => d.Description).Not.Nullable().Length(256);

        Map(d => d.HostedBy).Not.Nullable().Length(20);

        Map(d => d.ContactPhone).Not.Nullable().Length(20);

        Map(d => d.Address).Not.Nullable().Length(50);         

        Map(d => d.Country).Not.Nullable().Length(30);

        Map(d => d.Latitude).Not.Nullable();

        Map(d => d.Longitude).Not.Nullable();

        HasMany(d => d.RSVPs).KeyColumn("DinnerId").Cascade.All();

    }

}


Explaination of code:
  • The Table method maps "Dinners" as the name of the table. By default Fluent NHibernate assumes it to be "Dinner".
  • The Id method maps DinnerId as the primary key, and specifies that an identity column in SQL Server is used to generate ids.
  • The Map method is used to map simple properties to columns.
  • The HasMany method maps the one-to-many relationship to the RSVP table. Cascade.All() is used to make sure that RSVPs are saved/updated/deleded when the dinner is saved/updated/deleded.

The RSVP mapping

The code for the RSVPClassMap is shown below:

public class RSVPClassMap : ClassMap<RSVP>

{

    public RSVPClassMap()

    {

        Id(r => r.RsvpID).GeneratedBy.Identity(); ;

        Map(r => r.AttendeeName).Not.Nullable().Length(30);        

        References(r => r.Dinner, "DinnerId").Not.Nullable(); 

    }

}

Explaination of code:

  • No need to call the Table method, since Fluent NHibernate correctly assumes that the table name is "RSVP".
  • The References method maps the "other side" of the HasMany relationship in the DinnerClassMap.

Is all this mapping code needed?

The mapping code shown above contains a lot of information about the database schema. In this case, where we just wanted to map the domain to an existing database we could remove the following:

  • Not.Nullable()
  • Length(int length)

In Greenfield projects I prefer to start with the domain, and then write the mapping, and finally automatically generate the database schema. You then need to include this information in the mappings. In the post Mapping a Twitter like domain with Fluent NHibernate I showed how you generate the database from the mapping.

If you have an existing database you can also use this to troubleshoot your mapping code. Generate a new database from your schema and check it against the existing database.

Give me a new identity!

NerdDinner uses the identity column of SQL Server as primary key. In most cases this works just fine, however it can cause problems with the Unit of Work functionality of NHibernate. If I designed this application from scratch I would rather choose the Guid.Comb or HiLo algorithm to generate primary keys.

If you want to read more about this, check out the post NHibernate POID Generators revealed.

Do you prefer to live in XML hell?

If you miss the good old days of XML (???), then Fluent NHibernate can help you with this. The code below shows how to export your Fluent Mappings to XML-files:

[TestMethod]

public void ExportMappings()

{

    FluentConfiguration conf =

       NHibernateConfiguration.CreateConfiguration(false);

    conf.Mappings(m =>

    {

        m.FluentMappings.ExportTo(@"PATH_TO_EXPORT_FOLDER");

    });

    conf.BuildSessionFactory();

}

This technique is actually quite useful for several reasons:

  • First, if you are skeptical about using Fluent NHibernate because it has not reached 1.0, or because it might not support all mapping options, you can easily switch back to XML-files later.
  • Secondly, it can also be useful to troubleshoot mapping problems, because there is currently more information on the web regarding XML-mapping.     

finally{}

In this post we have taken a look at how to use Fluent NHibernate to specify the mapping between the domain model and the database. In the next post we will take a look at the rest of the changes that was necessary in order to use the new domain and Fluent NHibernate.

If you liked this post then please shout and kick me :)

kick it on DotNetKicks.com

Shout it

Book review: NHibernate in Action

Thursday, 4 June 2009 19:39 by bengtbe

Authors: Pierre Henri Kuaté, Tobin Harris, Christian Bauer, and Gavin King
Publisher: Manning Publications Co.
Purchase:
Manning, Amazon.com, Amazon.co.uk

Introduction

As part of my goal to learn ORM and NHibernate I purchased the NHibernate in Action book from Manning Publications. Before I give my conclusion, let's start with a summary of the different chapters in the book.

1. Object/relation persistence in .NET

This chapter starts the first part of the book, called Discovering ORM with NHibernate. Discusses object persistence, and briefly compares different approaches: hand coding, DataSets, LINQ-to-SQL, NHibernate, and ADO.NET Entity Framework. Describes Object/Relational mapping (ORM), and how it is used to solve the Object/Relational mismatch.

2. Hello NHibernate

Guides you through the process of setting up a project, creating a domain model, setting up the database, creating mapping file, configuring NHibernate, and doing some basic CRUD operations. Takes a high level look at NHibernate, its architecture and how it is configured.

3. Writing and mapping classes

This chapter starts the second part of the book, called NHibernate deep dive. Describes the most common scenarios when mapping a domain model to the database, including important concepts, such as object identity, inheritance, associations, entities vs. value-types, fine-grained object models, and transparent persistence. Finally, it shows three inheritance mapping strategies; table per concrete class, table per class hierarchy, and table per subclass. Covers both XML-mapping and attribute mapping.

4. Working with persistent objects

Describes the three object states (persistent, detached, and transient) that domain models have in a NHibernate application, and how they move from one state to another. It also gives an overview of different ways to get objects out of the database.

5. Transactions, concurrency, and caching

Shows how NHibernate handles transactions, short vs. long conversations, optimistic concurrency control and versioning. Finally it discusses caching and describes the first- and second-level cache that NHibernate provides.

6. Advanced mapping concepts

Goes into detail of more advanced mapping techniques, covering mapping types, custom mapping types, nullable types, enumerated types, collection of value types, mapping of entity associations, and mapping of polymorphic associations. 

7. Retrieving objects efficiently

Describes object retrieval with both HQL, Query by Criteria, and native SQL. Topics covered are parameters, comparison operators, logical operators, ordering, joining, report queries, dynamic queries, and query optimization.

8. Developing NHibernate applications

This chapter starts the last part of the book, called NHibernate in the real world. Discusses N-tier development by going through the business, persistence, and presentation layer. Finally it shows how NHibernate can be used to implement audit logging.

9. Writing real-world domain models

Covers domain-model development processes and tools, legacy schema mapping, understanding persistence ignorance, implementing business logic, data binding entities in the GUI, and filling DataSets from entities.

10. Architectural patterns for persistence

Focuses on the design of the persistence layer. Covers designing the persistence layer, implementing reusable Data Access Objects, implementing conversations, and using NHibernate in an Enterprise Services application.

Conclusion

First let's start with the things that I like about the book. As far as I know it is the only book published about NHibernate, and great OR/M deserves its own book. The book is well written and structured. It covers many aspects of NHibernate, and gives you enough information to start using NHibernate in your own project. The first part of the book is great for beginners of NHibernate. The second part covers more advanced topics and people with some experience with NHibernate should also find something useful in these chapters.

Unfortunately, there are some things that I don't like about the book. It covers NHibernate 1.2, even though 2.0 was released when the book came out. I also think that the last part of the book covers to much basic stuff about Domain Driven Design and N-tier development, instead of focusing on how to utilize NHibernate. Hopefully, the authors will release a second edition that covers version 2+, and also covers more community project, like Fluent NHibernate.

Overall I give this book 3.5 of 5 stars :)

kick it on DotNetKicks.com

Shout it

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