Home > .net 3.0, .net 3.5, c#, Microsoft, Oracle, SQL, Visual Studio 2008 > Adventures with Fluent NHibernate

Adventures with Fluent NHibernate

December 23, 2008

Fluent NHibernate LogoOver the past few weeks, I’ve taken our existing framework library and reevaluated how we connect to our student information system (SIS) to pull information.  Almost all of our applications read from SIS for some bit of information or another; however, nothing writes to it.  Changes are only handled through the application itself as the business logic is blackboxed by the vendor.

In the old library, connectivity was a bit piecemeal.  When someone needed something, we wrote a test, wrote the implementation PL/SQL, and slugged in some objects.  It worked, was fairly quick, and provided a standardized mechanism for querying the system (avoiding EVERYONE recreating what constituted a ‘student’).

Unfortunately, maintaining the object relationships to the magic strings of PL/SQL code became tedious (at best) and a change was needed.  I’d been evaluating NHibernate for a couple of projects (to replace LINQ-to-SQL) as Entity Framework still doesn’t have sufficient Oracle ODP support out of the box (without paying for another provider).  NHibernate seemed ideal, but XML configuration files made me cringe.

After a bit of syntax searching, I stumbled upon Fluent NHibernate (FNH)—an API that generates both the configuration and XML mappings and allows a MUCH better refactoring experience.  I’m all for ReSharper friendly!

As you may have read, I managed to work out an Oracle9 persistance configuration for FNH that has worked out quite well.  With that in hand, here’s a summary of how things hooked up.  Any feedback would be greatly appreciated as most of my learning has been, in my opinion, the best kind—trial and error.🙂

Also, I’m using various bits from the S#arp Architecture project; however, not everything—remember, I just need readonly access and the amount of noise for all of the libraries was far more than I needed.  I’ve also made several modifications to things such as the PersistentObject and NHibernateSession to include more specific casing required by our organization.

Note: I’m not using the AutoMap features of Fluent NHibernate.  Why?  At this time, our data structure is VERY wonky (vendor controlled data structure, no real rhyme or reason to it, readonly access), so I have a TON of “TheColumnNameIs” and such to map the properties to the oddball data field names.  I am going to hit up the AutoMap features on my next project to better understand them.🙂

Part #1 – [Code Here]

[Fact]
public void Fetch_A_Report_By_Id()
{
       IReportRepository stubRepository =
              MockRepository.GenerateStub<IReportRepository>(); 

       Report stubReport =

              MockRepository.GenerateStub<Report>();

 

       stubReport.Id = 140;

           

       stubRepository

              .Expect(x => x.Get(140))

             .Return(stubReport);

 

       var report = stubRepository.Get(140);

 

       report.Id.ShouldBeEqualTo(140);

}

A quick, simple test that looks for a repository and grabs a single entity based on Id. 

At this point, I need an IReportRepository that has a Get method and returns a Report entity.

public interface IReportRepository : INHibernateRepository<Report>

{

}

I could simply call NHibernateRepository<Report>() (or INHibernateRepository<T> in this case) directly; however, I prefer to create entity-based repositories.  I suppose if I had an entity that would NEVER have it’s own specific methods, then I’d reconsider, but I don’t like to mix access methodologies around.

Next, our Repository needs a Report object.  Remember, just the basics.

public class Report : PersistentObject

{

}

Since our Id field comes from the PersistentObject base class, adding it again is unnecessary.

With our Test, IReportRepository, and Report entity objects—we’re now flying green.  At this point, everything is mocked using Rhino Mocks (v3.5).

Passing Test!

Now, let’s try an integration test and hit the database using a bit of test data.

Part #2 – [Code Here]

[Fact]

public void Fetch_A_Report_By_Id_From_Database()

{

       var repository = new ReportRepository();

       var report = repository.Get(140);

       report.Id.ShouldBeEqualTo(140);

}

For this test, we need an implementation of our IReportRepository, ReportRepository:

public class ReportRepository : NHibernateRepository<Report>

{

}

The plumbing of the data connection also needs to be hashed out.  Fluent NHibernate makes configuring NHibernate very easy.

Setting up the Entity Map

Generating the mapping files is as simple as using a couple of virtual methods.  Id, Map, References, etc. will grow to be your friends.

public class ReportMap : ClassMap<Report>

{

       public ReportMap()

       {

              CreateMap();

       }

 

       private void CreateMap()

       {

              WithTable(“Reports”);

              Id(x => x.Id);

       }

}

In a few short lines of code, this has told the Fluent NHibernate API to create a mapping file for the Report entity, to use the Reports table, and to assign the data from the Id column to the Id property of the entity.  Good stuff.

Setting up the Configuration to the Database

Replacing the challenging XML syntax is a VERY useful, fluent configuration API.  I’m using a modified version of Init that reslugs in a default configuration since we’re packaging these configurations for internal use.

public static class ExampleConfiguration

{

       public static Configuration Default

       {

              get { return Production; }

       }

 

       public static Configuration Production

       {

              get

              {

                     var config = new Configuration();

 

              MsSqlConfiguration.MsSql2005

                    .ConnectionString

                        .Database(“ExampleDB”)

                        .Server(“DBServer”)

                        .Username(“DBLogin”)

                        .Password(“DBPassword”)

                        .Create

                    .ConfigureProperties(config);

              return config;

              }

       }

 

       public static void Init(ISessionStorage storage)

       {

              Init(Default, storage);

       }

 

       public static void Init(Configuration configuration,

                               ISessionStorage storage)

       {

              // Any of the maps will do for the assembly mapping.

              NHibernateSession.Init(

                     typeof(ReportMap).Assembly, configuration, storage);

       }

}

Default – Set which of the configuration is the default; called by the overloaded Init method.

Production – Set to the production; you could have Test, AnotherTest, or whatever else your environment needs.  The actual Configuration is using Fluent NHibernate’s PersistanceConfiguration to generate the configuration.

Init(ISessionStorage) – An overloaded Init that calls the default configuration and passes the parameter session storage to NHibernate; this calls …

Init(Configuration, ISessionStorage) – Calls the base NHibernateSession.Init.  This takes an Assembly, the configuration we’re passing it, and the ISessionStorage.

Is there a better way?  If there’s a better way to get the Assembly passed along, I’d LOVE to know.  I’m using this methodology since I keep all of my maps in the same library, but if one found its way into another library, this would break.

Initializing the Configuration

With that configured, I put the Init into the constructor for our test (using xUnit, constructors > [SetUp] :P).

public RepositoryBuildUpExample_2()

{

       ExampleConfiguration.Init(new SimpleSessionStorage());

}

SimpleSessionStorage is a S#arp Architecture snippet for mocking NHibernate; works fantastic.

In a production application, such as an ASP.NET Web site, you would initialize the session in Session_Start or Application_Start and pass along a storage mechanism to save it in .NET session state (or another means).

That’s it—everything’s in place, let’s rerun our test!

Fluent NHibernate Example #2

Excellent.

At this point, further requirements flush out:

  • additional “Get” methods which are added to the ReportRepository,
  • additional properties (columns) for the entity,
  • and, of course, the tests that determine what code is needed.

For the “finished” example, you can download the [code here].

%d bloggers like this: