Monday, December 10, 2007

Blame NHibernate, Why Not?

I’m currently assisting with another project that is experiencing some performance issues.  A lot of finger-pointing and blaming has been going on, and most of the parties involved just want to jump in and blame NHibernate.

I figured that we could optimize the way NHibernate is being used to help gain some perf. back in the application and hopefully restore its reputation at the client (and frankly, my company). 

Some info about the application:

It’s a WinForms application that communicates via Web Services to a service layer.  The service layer utilizes NHibernate over a domain model for persistence.  The domain types are serialized and used directly on the client.  (more on this later)

There are roughly 100 tables and almost as many mapped entities.  It is using NHibernate 1.0.2.

We did some basic benchmarking of the application so that we could quantify how effective various fixes were.

Startup time: 14 seconds.  Ouch!  I turned on the hibernate.show_sql setting in the config file and watched the SQL queries fly.  They were covering the screen!  I wrote a simple log4net IAppender that would count the number of log statements that contained a query and found out that 304 queries were being executed at startup.  Obviously something is wrong here, so I copied the queries to notepad and began to group them.

The domain of the application is shipping (like, in ships across the ocean).  So there is a concept of Terminals, Ports, Port Locations, and Geographic Locations.  If we load all of the terminals, we need to load the port for each one, the port location for each one of those, and the geographic location for each one of those.  It turns out that this many-to-one is not that bad.  You can get most of the data you need in one query using left joins.  The problem we faced was that GeographicLocations held an IList of all of its PortLocations.  And furthermore the PortLocation held an IList of all of its Ports. 

Picture trying to build up just a single terminal…

  • select terminal details and build up a Terminal
    • need a port, so build it up
      • need a port location, so build that up
        • need a geographic location, so build that up
          • not done?  Now we need all of the port locations for the geographic location
            • you guess it, now we need all of the ports for all of the portlocations you just loaded
              • now we need to get all of the terminals for all of the ports for all of the port locations, ….

This is starting to sound like that children’s story The House That Jack Built.

This is EXACTLY the reason that ORM’s have lazy-loading.  If you haven’t used it before, here’s how it works:  You load up the terminal, but it doesn’t fire a query for the port.  It gives you a stand-in object instead.  The second you access the terminal.Port property, *boom* – a database query is executed, the port is loaded, and you are returned a port.  This allows us to select a minimal amount of data, and is generally favorable unless you know ahead of time that you’ll need the port, in which case it’s faster to just query for it all at once.  Needless to say, you need to at least think about this issue while developing.

Lesson #1:  Understand Lazy Loading Or Be Doomed

But remember that our domain entities are being returned from a web service.  There is no way for NHibernate to open a database connection on the client.  Thus, we cannot utilize lazy loading at all.

I decided to remove the IList of port locations from the GeographicLocation class.  As it turns out, there was no code using this method, so it was a simple change.  I removed the list, removed the relevant mapping xml, and rebuilt.

The queries were reduced to 94.  That 5–minute change improved the application load time to about 6 seconds.  This is not the end of the story on improving startup time, but this was a big win at a very low cost.

There are likely other areas like this where we can break collections, but I probably will be breaking client code, and I will have to resort to adding a service method and utilizing NHibernate queries to provide the objects in a collection, rather than object graph traversal.

Lesson #2:  Don’t go crazy with your colleciton associations.  Possibly favor querying over object traversal for problem areas.

As a side-note to lesson 2, read Eric Evans’ Domain Driven Design for a much better understanding of aggregates, aggregate roots, and how to partition your domain model.

Then I started looking at the logs.  These were turned off in development, so nodody ever saw this.  I noticed that there were a ton of messages like “CodeDOM failed for class …..   unknown character `” — this was the reflection optimizer trying to compile some code on the fly to assist with getting/setting property values.  The code was listed along with the error and you could clearly see that this happened only on types that had a generic parameter.  I searched around and found that this was a bug in 1.0.2 that didn’t understand how to read the string representation of generic types.  It was fixed in 1.2.0, so I decided to upgrade. 

The upgraded version reduced the startup time to 3 seconds, and I’m pretty confident that we won’t get much better than that.  Sure we can reduce the number of queries a bit more, but we’re getting into the realm of acceptable values here.

How did we go from 6 seconds to 3 seconds?  Well the reflection optimizer had previously been turned on and was failing… once for every entitiy.  This caused an exception and easily slowed down the startup time.  Additionally, the reflection optimizer wasn’t helping out getting and settting property values so they fell back on reflection, whcih can contribute to overal runtime performance degredation.  The reflection optimizer is FAST, so let it do it’s job!  (see how fast in this article by Jay Chapman)

Lesson #3: Turn on logging (even in Dev!)

Lesson #4:  Take advantage of the reflection optimizer!

Next we took a look at one of the trouble screens and discussed how to make it faster.  The screen needed a lot of different types of data.  It had details at the top of the page, most of which were dropdown selections loaded by another entity, and the grid needed data from 3 different entities, flattened out.

The issue here is that the UI needs SO many different entities, that a signification portion of the entities are being loaded when only a subset of the data is actually being displayed.  This is an excellent opportunity for applying Screen Bound DTO’s.

A screen-bound DTO is a custom type that contains a flattened view of only the data I need to fulfill the needs of the screen.  I can utilize Projections to create slim entities that only contain name/value pairs for example.  I can accomplish this all with HQL.

This means less queries being sent to SQL server, and less data going over the wire.

Lesson #5:  Consider Screen-bound DTO’s instead of consuming your entities on the UI

On the database side, we can probably deal with a more friendly transaction isolation strategy, such as ReadCommitted.  This will allow more concurrent reads than other isolation levels.  This is accomplished through configuration.

Lesson #6:  Consider using ReadCommitted as your isolation level

We should also configure the default_schema setting to be databasename.dbo because that will be used to fully qualify the objects in all database queries.  Without this, SQL Server will not cache the query plan for these queries.

Lesson #7:  Always make sure you’ve set hibernate.default_schema

There are still more optimizations that we might make, by analyzing opportunities for cachine on both server side and the client, tweaking the mappings, and loading large data sets on a background thread to keep the UI responsive.

I believe that we will be able to achieve very acceptable performance with a lot of analysis and a tweak here and there.  Maybe then we can restore some faith in NHibernate, as it is a truly powerful persistence framework and it would be a shame to yank it because it was implemented poorly.

An excellent resource is Billy McCafferty’s article on NHibernate Best Practices with ASP.NET.  Be sure and read it if you haven’t already (it’s full of useful advanced tips on NHibernate.

Are there any perf. considerations I’ve missed?

Thursday, November 15, 2007

NHibernate Screencast - Persisting the Address Book

A few weeks ago, Evan Hoff posted a screencast on building up a domain model in a test-first manner.   We talked about it and thought it might be interesting to show how to apply persistence to this already functioning and tested domain model.

So take a look and tell me what you think!  It weighs in at 40 minutes (I tried to keep it under 30 but hey).

Friday, November 09, 2007

On Fluent Interfaces and Friction

So I decided to start that NHibernate Mapping DSL project and open-source it.  Right now it doesn't do anything, but you're free to take a look at http://www.assembla.com/space/nhibernate-mapping

I wrote all of this code test-first, and it allowed me to tackle the interface and flow the way I thought might be useful.  I ended up with something that was mildly usable like this:

IMapping mapping = Mapping.For<Product>()
.Identity("Id")
.Property("Name")
.Property("Price")
.Map();

So this allows me to quickly accept the defaults of the mappings, so NHibernate will assume that I have ...
  • the column names are the same as the property names
  • the type of the column matches the type of the property and it can be picked up via reflection
  • the identity generator of the primary key is Identity
  • the identity/properties have setters
  • the "Name" and "Price" columns are nullable
This isn't always true of course, so we need to be able to specify where we deviate from the defaults.  So how can we accomplish this using a fluent interface?  We could resort to adding more overloads to the Identity() method, so you might have....

IMapping mapping = Mapping.For<Product>()
.Identity("Id", "product_id", Access.NoSetterCamelcaseUnderscore, Generator.Native)
.Property("Name", "product_name", false)
.Property("Price", false)
.Map();
But we don't know how many settings the user might want to specify.  If we have 4 simple, independent settings, then we'd have to support 4! method overload options!  Yuck!

public IPropertySpecificationPredicate Property(string name);
public IPropertySpecificationPredicate Property(string name, string columnName);
public IPropertySpecificationPredicate Property(string name, string columnName, DbType type);
public IPropertySpecificationPredicate Property(string name, string columnName, DbType type, int length);
public IPropertySpecificationPredicate Property(string name, string columnName, bool nullable);
...
This is just the beginning.  What if you wanted to only specify the property name and the nullability?  We quickly end up in an anti-pattern I like to call overload explosion

We can do a bit better, right?  I started to gather my thoughts and came up with this syntax:

IMapping mapping = Mapping.For<Product>()
.Identity("Id")
.Column("product_id")
.Generator(Generator.Native)
.Map();
So now the Identity() method takes a single parameter, the name of the property.  Anything else is optional.  The return type is now something like an IIdentityBuilder interface, which accepts methods on altering the identity object that its building.  Here you can see that we only specified the settings that we wanted, nothing else.  We still had to have a way to "pop" the current object (and thus the entire mapping) so that we can indicate to the builder that we are done building the property and you can return the IMapping object next.  This feels awkward for now, but for now it's ok.

Let's extend this pattern.  Once I'm working with the IMappingBuilder interface, I can now see methods called Generator(), Access(), Column(), etc.  There's nothing there to prevent me from calling .Column("...").Column("...").Column("...").Column("...") which I guess isn't such a big deal.  All it's doing is setting the column property of this object we're building.

Now I'm noticing that there has to be a spot where the user gets finished with the mapping identity and now has some choices.  The obvious next operation would be to create a property, but how do I pop the current context and start working on a new one?

Maybe something like this?

IMapping mapping = Mapping.For<Product>()
      .Identity("Id")
      .Column("product_id")
      .Generator(Generator.Native)
      .AndProperty("Name")
      .Nullable(false)
      .Map();

I'm already started to notice that my identity and property elements are getting lost in the noise.  If I keep up this pattern not only do I have TONS of ISomethingPredicateBuilder objects, I also remember that it's not only properties that I can add at any time.  I could add a Bag, a Set, a List, or any number of other mapping elements.

Another route I might take is using anonymous to provide me with the flexibility of code within the interface.  Take a look:

IMapping mapping = Mapping.For<Product>()
      .Identity("Id", delegate(Identity i) {
                         i.column = "product_id";
                         i.generator = Generator.Native;
                      })      
      .AndProperty("Name", delegate(Property p) {
                         p.column = "product_name";
                         p.nullable = false;
                      })
      .Map();
Here I have a lot more flexibility over the different ways that I define various mapping elements.  The only part of this that is really cumbersome is the delegate syntax itself.

This is probably where Boo or Ruby would come in and save the day because of it's super-dynamicness-flexibility-extraordinaire, but I'd like to see how far I can push C# for now.  At least until C# 3.0 comes out, where I'll get object initializers, lamba expressions, and a few other neat tricks that really make this stuff more fun to sculpt.

What about you?  What types of syntax do you find most readable, usable?
Friday, October 26, 2007

Thinking of an NHibernate Mapping DSL

Something about Ayende inspires me to build something.  I got to thinking today about how I could create a DSL for NHibernate mapping files.  What's wrong with XML, you say?  Well...

  • It's verbose - XML is known for it's ability to easily validate based on a schema, not for readability.  In other words, XML was designed for computers, not humans.  Since I am a human (are you?) I would prefer something a bit more friendly.
  • Schema Validation takes time - Usually you don't notice, but some recent metrics show that it can account for more than 1/2 of the startup time for NHibernate when there are many mapping files.  If you have an appliaction with 1000 entities, arguably you have bigger problems than XML's readability and schema validation performance, but it IS noticable so hey....
  • Generates runtimes errors for semantic structure - Sure, the syntax is validated, but there are so many things that can go wrong with runtime compilation (that's a weird term) of mappings.
So I got to thinking how we could do better.  Here's what I came up with (with some additions from Ayende) -- it's in Boo:

import flux88.NHibernate.Mapping

mapping Test.Project, Test.Project.Entities

entity Customer,
table: Customers
default_access: "nosetter.camelcase-underscore"entity Customer,
     
        identity "Id",
generator: "native"
       
        property Name,         
column:   "CustomerName",
type:     "String(12)",          
nullable: false

property DateJoined,

property Address,
type: "String(200)"

set _orders,
access: "field",
manyToMany Order,
table: Orders,
pk_column: "customer_id",
fk_column: "order_id",
inverse: true

entity Order,
table: Orders

Note that there isn't a ton of extra fluff, it's meant to be light-weight and easy to write.  Additionally you could set defaults at the top (like default_access) so that you can avoid typing it all over your mappings.

Ayende added the notion of doing this:

for type in assembly.GetTypes():
continue if type.Namespace != "My.Model"
entity type.Name, type.Namespace:
idenity "id"

for prop in type.GetProperties():
property prop.Name

You get the idea.

There could be a number of benefits of doing it like this.  First, you can compile it, so you can get syntax errors notified.  If you use the BooExplorer I'm pretty sure you'd get intellisense for this.  I could also see plugging this into a model validator that would reflect over the model and make sure that all of the properties and access strategies will work with the mapping as defined.

Anyway, I have some reading up on DSL's to do... Ayende, that means I need you to write that book on DSLs with Boo :)  Can I have it next week?

Thoughts?  Suggestions?

Sunday, August 26, 2007

HoustonTechFest 2007 was a Hit

Houston Tech Fest 2007 was yesterday and it was an amazing success.  Over 600 people were present and I was able to see a lot of old friends and meet some new & interesting people.

I got to chill out with Brad Abrams and a few other folks from Microsoft, and it was great to meet Scott Bateman (fellow Houston blogger and advocate of Continuous Integration).  His talk was most excellent, he was very informative and it showed that he not only understood the concepts, but he was able to explain it all in a way that (I thought) was easily grasped by the audience.

Some of the other speakers included Mike Azocar, John Cook, Tim Rayburn (sorry I missed your talk again Tim!), and David Walker.

Funny thing about Tim’s talk.  I was in the room next door listening to David Walker talk about SOA and WCF, and we could hear Tim through the wall partition.  At one point I heard Tim say, “If you can’t hear me in the back, please shout out and let me know.”  I felt like shouting from our room .

I had two sessions, Advanced CSS & Javascript and ORM with NHibernate.  In the first session, I had a full room (over 100 people!) and the audience was very engaging.  It’s great to have an audience that has so many questions and comments on what you are saying.  It becomes more of a dialog, which I think makes the content so much more valuable for everyone. 

Sometime in the midway point of the talk the projector just turned off.  It’s hard to give a talk on CSS without some visuals, so I continued typing and said “Imagine if you wil…” to a room full of laughter.  About that time the projector turned on again.  I didn’t find out until later, but my friend Wes had sat on the cord and unplugged it, then silently plugged it back in.  Funny how a room full of geeks and nobody thought to check the plug when it turned off.  Ah well.

At the end of the talk I had a couple people come up to shake my hand and a little 4–year old girl came up, touched my arm, then ran away giggling.  (Don’t ask me…).  I really enjoy giving this talk and I would like to branch outside of Houston and start giving it elsewhere around Texas (and the U.S.).

My second session was on NHibernate and it was more cozy, with only 15 people or so.  I think NHibernate is a lot harder to make interesting for an intro class because it is a radically different way of developing applications that most people are exposed to.  There is so much material to cover and only a short amount of time, so I tried to focus on the code and keep the examples short and simple.  I think it was effective, though I’d like to branch off this talk and start giving more advanced lectures on NHibernate.

Thanks to all who came to the event (especially my sessions!).  I hope you all had as much fun as I did.

The files from my presentations can be downloaded here:

File Attachment: AdvancedCSSAndJavascript.zip (1161 KB)

File Attachment: nhibernate.zip (4001 KB)

Also, a lot of people were interested in the tools I was using during my demos.  My favorite text editor is E, which you can get at http://www.e-texteditor.com.  My zooming tool that I use is called Zoomit.  My launching utility is called Launchy and it saves me so much time everyday.  I use ReSharper for Visual Studio nirvana (seriously, get this tool).

Some links I mentioned at the talks:

http://www.csszengarden.com

http://www.exploding-boy.com

http://www.mochikit.com

http://www.prototypejs.org

And finally, my favorite CSS book that I keep recommending to everyone:  CSS Mastery.

Wednesday, August 15, 2007

See you at Houston Tech Fest

If you're going to HoustonTechFest, then be sure to check out my presentations:
  • Advanced CSS and Javascript
  • ORM with NHibernate
It's all going down August 25th at the University of Houston.  See you there!

Thursday, August 02, 2007

Excellent Advice from a Hibernate Expert

I just stumbled on this list of 10 Things I Learned About Using Hibernate Successfully.

It is excellent advice for anyone using Hibernate or NHibernate.  I fully agree with each point on this list.

http://www.spenceruresk.com/2007/07/27/10-things-i-learned-about-using-hibernatejpa-successfully/

Sunday, July 29, 2007

My Presentation at OKCodeCamp - DDD with NHibernate

I presented a talk on Domain Driven Design with NHibernate at the OKC code camp yesterday.

It started out a bit rough because I had some missing references and some confusing exceptions, however once I got the ball rolling I think it went pretty well.

I ran way short on time, however, and I promised the crowd that I would post the completed files on my blog tonight .

So, here is the powerpoint, demo applications, and all of the referenced dlls from my presentation.  Let me hear your feedback in the comments!

File Attachment: ddd with nhibernate.zip (7694 KB)

(the file is large because I have the complete references to specific versions of NHibernate and NUnit, so you shouldn’t have to worry about references at all.)

To get the sample running, change the app.config to point to an actual database.  The database should be empty.

Thursday, July 19, 2007

Speaking on NHibernate in OKC

I turns out that Jeremy Miller can’t make it to the code camp.  I will be taking his slot and giving a talk on NHibernate.  Specifically I will be talking about how to apply Domain Driven Design principles and address persistence with NHibernate.

I am bummed I don’t get to meet Jeremy, however I am glad to have the opportunity to speak.

Sunday, June 03, 2007

A Journey with Domain Driven Design (and NHibernate) - Part 9

Back by popular demand… (and WAY too late, I might add…)

In this article, part 9 of the series, we’re going to wrap up our initial feature list and focus on building a user-interface for our video store, named Videocracy.

Here is our feature list:

  • Add new Customer / Account
  • Add other members to an account
  • Restrict Certain members from renting certain content
  • Query for a customer by customer # (swipe card, etc), phone number, or last name
  • Add new rental item (move game, console, vcr, etc)
  • Rent an item to a customer
  • Check Items Back in
  • Get movies checked out for a customer
  • Query for an item, see who has it

Let’s implement Check Items Back In.

What is the use case here?  Typically a customer drops off a movie, either in the slot from the outside of the store, or directly to an employee.  Either case starts with the movie, or the Item.

So the employee is going to scan the item.  At this time, they are going to need to see the details of who and when the item was checked out (the Rental object), and then have a confirmation button to finalize the check-in (which will update the Rental object).

In order to facilitate scanning the item, we need to be able to query the database for a particular UPC.

In implementing this next test I noticed a large problem with my domain.  Our Item class defines a name property with a mapped column in the database.  The problem with this is that not all items have names of their own.  They might have display names that are a combination of other properties.  For example, a video copy of the movie The Matrix shouldn’t have a name of ‘The Matrix’ (it really belongs to the Movie class).  Typically the text on the outside of a video rental is the movie name followed by maybe the year, and the format that the movie is in.  These are deferred properties, so there is no more need for a database column in the item class.  I still provide a read-only Name property, and inherited classes have to provide this information.  Would you have made a large change like this so eagerly in your project?  If you did, how would you know if you broke some existing functionality?  As I always say, it’s a good thing I have the tests to back me up…

Here’s our next test:

      [Test]

        public void Can_Check_In_Item()

        {

            Item i = TestHelper.CreateTestItem("Test_ITEM123", "1234_UPC");

            Customer c = TestHelper.GetTestCustomer();

            Employee empl = TestHelper.CreateTestEmployee();

 

            using (ISession session = SessionSource.Current.GetSession())

            {

                using (ITransaction tx = session.BeginTransaction())

                {

                    //store the customer and employees

                    session.Save(c);

                    session.Save(empl);

 

                    //check the item out to the customer

                    Rental r = new Rental(i, 2.30f);

                    RentalTransaction trans = c.CreateTransaction();

                    trans.Employee = empl;

                    trans.AddRental(r);

 

                    session.Save(c);

                    session.Save(trans);

 

                    tx.Commit();

                }

            }         

 

            //item is checked out, can we check it back in?

            Item i2 = Repository<Item>.FindSingleByProperty("UPC", "1234_UPC");

            Assert.IsNotNull(i2); //just a sanity check.  Item exists in the table.

 

            //we need to get the rental by the item upc

            Rental r2 = new RentalFinder().FindByItemUPC(i2.Upc);

 

            Assert.IsNotNull(r2, "Rental was null!");

        }

There is a lot going on in this test, so let’s break it up and take a look at each part.

            Item i = TestHelper.CreateTestItem("Test_ITEM123", "1234_UPC");

            Customer c = TestHelper.GetTestCustomer();

            Employee empl = TestHelper.CreateTestEmployee();

 

            using (ISession session = SessionSource.Current.GetSession())

            {

                using (ITransaction tx = session.BeginTransaction())

                {

                    //store the customer and employees

                    session.Save(c);

                    session.Save(empl);
                    . . .

Here we setup our environment.  We have to have a lot of entities that already exist in order to test our new funtionality.  We need an item, a customer, and employee, and an account.  We create all of those and save them.  (Remember all of this happens within a transaction that is rolled back, so this doesn’t stay in the database).

 

                    //check the item out to the customer

                    Rental r = new Rental(i, 2.30f);

                    RentalTransaction trans = c.CreateTransaction();

                    trans.Employee = empl;

                    trans.AddRental(r);

 

                    session.Save(c);

                    session.Save(trans);

 

                    tx.Commit();

 

Here we setup the rental and perform the rental transaction.  This is how the UI will structure the business process.  Once everything is saved, it’s time to verify that we can retrieve the data solely based on the item’s upc code.

            //item is checked out, can we check it back in?

            Item i2 = Repository<Item>.FindSingleByProperty("UPC", "1234_UPC");

            Assert.IsNotNull(i2); //just a sanity check.  Item exists in the table.

 

            //we need to get the rental by the item upc

            Rental r2 = new RentalFinder().FindByItemUPC(i2.Upc);

 

            Assert.IsNotNull(r2, "Rental was null!");

The Rental Finder class will encapsultate common queries so that we can reuse them across the application.  Here’s the code for that method:

public Rental FindByItemUPC(string upc)

{

    Rental r = Repository<Rental>.FindSingleByQuery("from Rental r where r.Item.Upc = :upc and r.DateReturned is null", new Parameter("upc", upc));

    return r;

}

There is a lot going on here in this test, but what you need to get from this is that we are setting up the stage for our scenario.  As our tests get more involved we are verifying that business cases are being met.  If we make drastic changes later on, we will know if we have broken existing business functionality.

The next thing to check is to make sure that we can check the item back in.  I add a .Return() method on the rental, which doesn’t exist yet, so I need some additional tests.

I also need to be able to calculate late fees in a central place to make that easy to change later.  I add a few tests for this as well.

It turns out that the Return() method is easy to implement.  All we need to do is set the return date and calculate the late fee and save it.

[Test]

public void CanReturnItem()

{

    Rental r1 = GetTestRental(DateTime.Now.AddDays(-6));

 

    r1.Return();

 

    Assert.IsTrue(r1.DateReturned.HasValue);

 

    //it's 1 day late, so expect the right late fee

    float lateFee = Utility.LATE_FEE_PER_DAY;

    Assert.AreEqual(lateFee, r1.LateFee);

 

}

which leads to the following code in the Rental class…

public void Return()

{

    this.DateReturned = DateTime.Now;

    this.LateFee = Utility.CalculateLateFee(_dateDue, _dateReturned.Value);

}

Now we need to finish the original test and verify that we can save the rental.

 r2.Return();

 

 //save the rental

 using (ISession session = SessionSource.Current.GetSession())

 {

    using (ITransaction tx = session.BeginTransaction())

    {

        session.SaveOrUpdate(r2);

        tx.Commit();

    }

 }

This test passes and we’ve implemented our feature!

I think I’m at a point where I have demonstrated how we can work on core business features for an application test-first, using NHibernate along the way for persistence.  A lot more work has to be done to complete our domain model, but that will be left as an excercise for the reader.

Instead, I would like to focus my efforts on getting a basic UI in place using ASP.NET.  I said in part 1 that I wanted to demonstrate how to work with the NHibernate Session in a web environment, so that’s where I will pick up next time.

Until then, you can download and view the current project here:

File Attachment: Videocracy_09.zip (3442 KB)

Wednesday, April 11, 2007

My Presentation on ORM with NHibernate

A couple of weeks ago I gave a presentation on Object Relational Mapping with NHibernate.

We talked about:

  • Basic Concepts
  • Getting Started, watching what SQL gets generated for various actions
  • Architecture, querying, etc
  • A live Blog example done with TDD

A number of people came and watched me drown in code, but I think all-in-all it was a good time and I think I conveyed a lot of information in a small time-frame.

Anyway, I recorded the presentation.  You can download it below.  You will need the TechSmith Video Codec to view it.

A few times I was asked questions by the audience and I didn’t repeat the question, so hopefully you will be able to infer a bit. 

I’d appreciate your feedback if you watch it, it’s about 1 hour and 45 minutes (ouch!).

File Attachment: ORMWithNHibernateScreenCast.wmv (64824 KB)

 

Wednesday, April 04, 2007

Billy Does It Again With NHibernate Best Practices

If you’re interested in NHibernate, or are already using it then run over to Code Project and check out Billy McCafferty’s updated article on NHibernate Best Practices with ASP.NET (1.2).

Billy hits the nail on the head with a number of issues and is able to address “real” development topics in depth.  He obviously knows his stuff.

Monday, November 13, 2006

A Journey with Domain Driven Design (and NHibernate) - Part 7

After a very long spell of inactivity, we’re back for Part 7 of the series!

Last time we left off with a decent domain model, a database, and the beginnings of NHibernate.

Since the last article, the NHibernate team has produced the ever-so-fantastic v1.2.0 beta 1.  This release supports numerous new exciting features.  Topping the list of cool new features is:  native support for .NET GenericsNative SQL 2005 is also supported.  Another feature was just enabled that will really help bridge the gap for those folks who will clutch to stored procedures forever:  Stored Procedure / Custom Query Support.

I’ll start today by slapping in this version of NHibernate and do a little bit of housekeeping.  With this new version of NHibernate, I no longer have a need for NHibernate.Generics (and thus I will remove it).  Some people might still use it, as it has the nice side-effect of automatically managing bi-directional relationships (which we’ll visit later), however I’m going to show this manually in this series.

I’ve changed my EntityList<T> / EntityRef<T> / EntitySet<T> to normal generic collections, and NHibernate will use them automatically.  These classes do provide you with an extra nifty feature (explained later), but it’s important to understand what it does under the hood, so I will implement the functionality the manual way, and later I’ll reference these classes to show how you might still want to use them.

So I’ve remove the old dll of NHibernate and I referenced in the new one.  I also updated my nhibernate schema file in Visual Studio so that I get the updated intellisense.  I compile, nothing is broken, and we’re ready to test!  The beauty of having our unit tests is that we have much more confidence that we know exactly what to fix, and we also know the instant that we are done fixing any breaking changes.  Our application returns to a known state of behavior very quickly.

Since I’ve made persistence level changes (the new NHibernate dll) as well as domain model changes (replacing NHibernate.Generics.Entity*<T> with standard .NET generic lists) I need to run both test assemblies.

 Here are the immediate results of the tests:

TestCase 'Flux88.Videocracy.Persistence.Tests.PersonTester.TestCanListPeople' failed: NHibernate.InvalidProxyTypeException : Type 'Flux88.Videocracy.DomainModel.Person' cannot be specified as proxy: method get_Id should be virtual
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Proxy\ProxyTypeValidator.cs(28,0): at NHibernate.Proxy.ProxyTypeValidator.Error(Type type, String reason)
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Proxy\ProxyTypeValidator.cs(76,0): at NHibernate.Proxy.ProxyTypeValidator.CheckMethodIsVirtual(Type type, MethodInfo method)
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Proxy\ProxyTypeValidator.cs(63,0): at NHibernate.Proxy.ProxyTypeValidator.CheckEveryPublicMemberIsVirtual(Type type)
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Proxy\ProxyTypeValidator.cs(22,0): at NHibernate.Proxy.ProxyTypeValidator.ValidateType(Type type)
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Cfg\Configuration.cs(915,0): at NHibernate.Cfg.Configuration.ValidateProxyInterface(PersistentClass persistentClass)
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Cfg\Configuration.cs(891,0): at NHibernate.Cfg.Configuration.Validate()
	c:\net\nhibernate-1.2.0.Beta1\nhibernate\src\NHibernate\Cfg\Configuration.cs(1035,0): at NHibernate.Cfg.Configuration.BuildSessionFactory()
	c:\sandbox\Videocracy\Flux88.Videocracy.Persistence\SessionSource.cs(55,0): at Flux88.Videocracy.Persistence.SessionSource.Initialize()
	c:\sandbox\Videocracy\Flux88.Videocracy.Persistence\SessionSource.cs(81,0): at Flux88.Videocracy.Persistence.SessionSource.GetSession()
	c:\sandbox\Videocracy\Flux88.Videocracy.Persistence.Tests\PersonTester.cs(27,0): at Flux88.Videocracy.Persistence.Tests.PersonTester.TestCanListPeople()

We have a few failing tests, however they all relate to the same problem.  NHibernate 1.2.0 beta 1 now sets everything to lazy-load by default.  This means that our objects must be able to be inherited so that NHibernate can inject proxy subclasses of our objects (the consumer doesn’t know or care that this happens).  To do this all of the methods and properties need to be virtual.   This is a large change, and it’s not something I entirely agree with, but it’s very easy to turn off.   (It pays to read the release notes of these 3rd party products!)  We’ll discuss how to effectively lazy-load a bit further on.

In our mapping file, we can specify default-lazy=”false” to override this behavior.  At a later time we may investigate and find that the class will benefit from lazy loading and we’ll revert it, but until then I’ll just make all the classes default-lazy=”false”.

Here’s the change:

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"

                  namespace="Flux88.Videocracy.DomainModel"

                  assembly="Flux88.Videocracy.DomainModel"

                  default-lazy="false"

                  >

 

  <class name="Person" table="People">

     …

  </class>

 

</hibernate-mapping>

Once that change is in place, all 5 of my persistence tests are passing.   I have 6 failing tests in my domain model, mostly due to my careless replacement of the EntityRef stuff with standard .NET collections.  The tests expose my oversights and once I fix them I’m back on track.

Ok, now where were we?  (It’s been a while, so I open NUnit and see that we have Save/List/Load for a Person object, but nothing else). We introduced some simple mapping concepts, and this time we’ll dig in a bit deeper.

How does NHibernate deal with associations?  Lets start with Account.  Account has a collection of Customer objects that represent its members.  In standard database terms, Account is a one-to-many with Customer.  How do we map this kind of relationship with NHibernate?

There are a few choices when looking at the documentation for NHibernate.  To understand them you must understand the semantics of each kind of list.  Ask yourself these questions:

  • is it one-to-one?  many-to-one?  many-to-many?
  • does order matter?
  • is it bidirectional?

From the NHibernate FAQ, we find this informative nugget, that explains the collections available to us (and their .NET counterparts):

  • The <list> maps directly to an IList.
  • The <map> maps directly to an IDictionary.
  • The <bag> maps to an IList. A <bag> does not completely comply with the IList interface because the Add() method is not guaranteed to return the correct index. An object can be added to a <bag> without initializing the IList. Make sure to either hide the IList from the consumers of your API or make it well documented.
  • The <set> maps to an Iesi.Collections.ISet. That interface is part of the Iesi.Collections assembly distributed with NHibernate.

Our Account -> Customer relationship is one-to-many.  I could choose to allow one customer to have many accounts, but I don’t see any real use for that yet.  Order does not matter either.  I could have 4 members on an account, and it doesn’t really matter what order they are given to my in a list. 

We also have to consider what each end of the association looks like.  Right now I’m looking at it from the perspective of Account, where Account has many Customers (members).  On the customer object, the customer only has one Account, so this isn’t a bidirectional association.  (We’ll see an example of this later).  Techically our collection maps to an ISet, but I’d rather not depend on the Iesi.Collections.dll, so I’m going to map it to a list (the only drawback is that the list will allow the same member twice, so I’ll just get around this in our model manually).

Here’s how we’ll deal with that:

/// <summary>

/// Adds a member to this account

/// </summary>

/// <param name="customer"></param>

public void AddMember(Customer customer)

{

    //ignore multiple adds for the same customer.

    if(_members.Contains(customer)) return;

 

    _members.Add(customer);

 

    //make sure that our association is handled on both sides

    customer.Account = this;

}

 

/// <summary>

/// Removes a member from this account.

/// </summary>

/// <param name="customer"></param>

public void RemoveMember(Customer customer)

{

    //ignore the call if the customer isn't a member of this account

    if (!_members.Contains(customer)) return;

 

    _members.Remove(customer);

 

    //make sure that our association is handled on both sides

    customer.Account = null;

}

 The rest of the class is self-explanatory, so I won’t include it here.  You’ll  be able to download the project at the end of today’s article, however.

Next up is the mapping.  Let’s take another look at the table structure for reference:

CropperCapture[4]

This is slightly different than what was in the last archive.  Instead of an account having a single primary customer, any customer can be the ‘Owner’ of his/her account.  I chose to keep the association simple for this demo.  I also wanted to allow multiple ‘Owners’ for a single account.  I’ve updated the tests to reflect this change.

Here you see that many customers belong to exactly one account.    This is a one-to-many relationship from Account to Customer.

If we stop and think about the domain for a minute, we can determine that the Customer will be identified first (by his/her ID or Account #) and that needs to be able to pull up the account.  I’d also like to keep this relationship bidirectional.  That means that I need to map the relationship on both classes so that we can do this:

customer.Account ….  

or this:

for each Customer c in _account.Members { … }

Here is the relevant mapping for Customer:

 <!-- CUSTOMER  -->

 <joined-subclass name="Customer" table="Customers">

  <key column="PersonId" />

 

  <!-- todo:  map this to an enumeration -->

  <property name="RentalRestriction" type="Int32" column="rentalRestrictionId" />

 

  <many-to-one name="Account" class="Account" column="accountId" cascade="save-update" />

 

 </joined-subclass>

This tells NHibernate that this is the *many* end of a one-to-many association.  We tell it the property name, type, foreign key column name, and our cascade strategy.  This basically means, whenever we save or update our Customer, cascade the save/update to the Account as well.  You can also specify to cascade deletes as well if you need to.

The Account mapping looks like this:

 <class name="Account" table="Accounts" where="Active=1">

 

    <!-- ... -->

 

    <bag name="Members" access="nosetter.camelcase-underscore" lazy="true" 
         
cascade="save-update" inverse="true">

         <!-- this key refers to the Account key in the Customer table -->

         <key column="accountId" />

         <one-to-many class="Customer" />     

    </bag>

 

 </class>

This section tells NHibernate to map this association as a bag.  Lazy=true means that the collection will only actually be retrieved from the database once it is accessed.  NHibernate does this by silently replacing our IList with a proxy that knows about the session and how to hydrate the collection.  This is 100% transparent to our consumers, so make sure you are using this in appropriate places.  Lazy=false will force NHibernate to pre-load the entire association (and it’s associations(and it’s associations(…. you get the point…))). 

To test that this association worked, I wrote a simple test:

 [Test]

 public void CanSaveCustomerAndAccount()

 {

    Customer c = TestHelper.GetTestCustomer();

 

    Account a = new Account();

    a.AddMember(c);

 

    using (ISession session = SessionSource.Current.GetSession())

    {

        using (ITransaction tx = session.BeginTransaction())

        {

            session.SaveOrUpdate(c);

            session.Flush();

 

            session.Evict(c); //make sure we don't get an object from the cache

 

            Assert.AreNotEqual(0, c.Id, "Customer didn't get an Id after save");

            Assert.AreNotEqual(0, a.Id, "Account didn't get an Id after save");

 

            Customer customerFromDb = session.Get<Customer>(c.Id);

 

            Assert.AreNotSame(c, customerFromDb, "Got same instance of customer!");

            Assert.AreEqual(a, customerFromDb.Account, "Account didn't get saved");

 

            tx.Rollback();

        }

    }

 }

This test is passing and we have our first association mapped!

I think this is a good stopping point.   We have migrated our version of NHibernate and our tests forced us to fix any outstanding issues before continuing.  We also identified our first association, mapped it, and tested it.

From here on out I will step up the pace and start glossing over the details a bit and focus on getting working product done using this domain model.  As I begin writing some UI components I will come across some features I have not developed, which may require more mapping changes and tests.  In order to finish this series sometime this year I’ll be speeding up a bit.  I will still continue to post the source so that you may follow along.  If you have comments or questions, feel free to comment.

Here is the latest source:

File Attachment: Videocracy_07.zip (44 KB)

Friday, September 01, 2006

A Journey with Domain Driven Design (and NHibernate) - Part 6

Welcome back!  In Part 5 we finished off with a table structure and the beginnings of NHibernate.  We’ll dive in deeper with NHibernate and mapping in this article.

(part 1 was Dugg, which was quite exciting to me!  It has also increased the traffic around here a little bit!  If you’re enjoying the series, why not Digg it some more?)

Currently we can initialize our SessionFactory which reads the configuration file and loads our domain model assembly.  This step is where NHibernate will parse our mapping files (once we add them).

So let’s take a step back and ask the question that’s probably on your minds already: how do we get our classes mapped to the database using NHibernate?  We have to provide a mapping file.  An NHibernate mapping file is an xml file that specifies how your object fields and properties map to database columns.  There are a few ways you can load mapping files into NHibernate, but the easiest I have found is to add them as part of your domain model class library as embedded resources (details below).   I emphasize embedded resources because if you just add the XML files without specifying they will not be recognized automatically by our SessionFactory initialization.

A number of people have asked me to display more source code, and in this article you’ll see the domain model in more detail.  At the end of this article I will post the code that we’ve created so far.

If you are a little scared about writing these mapping files and would rather use a tool to generate it for you that is certainly possible.  Ayende Rahien’s NHibernate Query Analyzer can help with this.  I have found, however, that in order to fully understand how to effectively map your objects, you should write the mapping by hand.  Don’t fret, there is hope with the manual method.  The hope?  Intellisense.  To get nhibernate mapping intellisense in Visual Studio 2005 you can copy the schema file (nhibernate-mapping-2.0.xsd) to C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas.

Now that we have intellisense for our mapping files, we can start creating our mappings.  I choose to have a single mapping file per class, so that they don’t get too cluttered.  There is nothing to stop you from defining your entire object model in a single mapping file, but I avoid this approach.

CropperCapture[26]Starting off with the Person class, we create add a new XML file to our domain model project called Person.hbm.xml.  NHibernate will search our assembly for embedded resources named *.hbm.xml.  If this is mispelled or the file is not marked as an embedded resource the mapping will not be found and NHibernate won’t recognize the object.

 CropperCapture[28]
In these screen shots you can see that I have added the mapping file next to my objects and I right-clicked and selected properties.  Then under the properties pane I clicked on Build Action and changed the value from Compile to Embedded Resource.CropperCapture[27]


 

Let’s take a side-by-side look at our Person class with our People database table.  This will be  a simple mapping because the class contains no complex associations with other classes.

Side-by-side compare3

Here you can see that we simple relate each property in our class to a column in the table.  Address, in this example, is called a component.  What this means is that Address has no table of its own;  all of the address properties are mapped to the containing object’s table.  We say that Address is a component of Person.  I chose to represent it this way in the object model to delineate the model and separate concerns.  It is a common occurrence for the database, however, to not employ unnecessary joins for simple associations such as this one (for performance reasons).

We start by opening our (now blank) Person.hbm.xml file.  In order to tell Visual Studio that we want intellisense for this file, we have to create the document element (the root) called <hibernate-mapping>.  We tell it which schema to use, and then intellisense kicks in.  Also in this root element the assembly and namespace are defined.  This saves us from typing it every time in the mapping file.  For example, within this mapping file, Person will refer to the strongly typed Flux88.Videocracy.DomainModel.Person, Flux88.DomainModel.Person (see?  less typing!).

*note:  I have changed the Phone number properties to be plain-jane strings instead of the Phone Number class that you may have seen earlier.  While this is a good addition, it complicates the mapping a tad too much right at first, so I removed it in favor of a simple mapping for now.

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"

                  assembly="Flux88.Videocracy.DomainModel"

                  namespace="Flux88.Videocracy.DomainModel">

 

</hibernate-mapping>

The next element in this mapping will be our class:

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0"

                  namespace="Flux88.Videocracy.DomainModel<