Showing posts with label NHibernate. Show all posts
Showing posts with label NHibernate. Show all posts

Friday, 10 April 2009

Convention over Configuration

In this post I would like to introduce design pattern which is particularly close to my heart - Convention over Configuration. What I like the most about this pattern is that it eliminates lots of monkey code which we have to write from time to time. Firstly let me explain to you what I mean by monkey code.

Monkey code

Typical case of monkey code can be found in old-fasihion NHibernate mappings:

   1:  <hibernate-mapping>
   2:    <class name="Product" table="tblProduct">
   3:      <id name="Id" type="Int32" column="ProductID">
   4:        <generator class="identity" />
   5:      </id>
   6:      <property name="ProductLine" type="String">
   7:        <column name="ProductLine" length="2" />
   8:      </property>
   9:      <property name="Class" type="String">
  10:        <column name="Class" length="2" />
  11:      </property>
  12:      <property name="Size" type="String">
  13:        <column name="Size" length="5" />
  14:      </property>
  15:      <property name="DaysToManufacture" type="Int32">
  16:        <column name="DaysToManufacture" not-null="true" />
  17:      </property>
  18:      <property name="ModifiedDate" type="DateTime">
  19:        <column name="ModifiedDate" not-null="true" />
  20:      </property>
  21:      <property name="ListPrice" type="Int32">
  22:        <column name="ListPrice" not-null="true" sql-type="money" />
  23:      </property>
  24:    </class>
  25:  </hibernate-mapping>

Even though in most cases column name and property name are the same, we still have to specify them. There is nothing really creative in this code. I think it's general problem for all sorts of mappings, it doesn't really matter if they are in a form of XML or a plain-old-CLR-objects - someone has to write this pesky piece of code. If you multiply time needed to map single column by number of columns and then by number of tables then you will get huge waste of time.

Conventions

I like Fluent NHibernate so much because it uses conventions, for instance I can say that all my tables have the same names as my classes and let fluent-nh map everything for me.

Within one application there might be number of aspects to which conventions can be applied. For instance, in fluent-nh it might be:

  • class name vs table name
  • property name vs column name
  • reference property name vs foreign key column name

Another project using conventions is ASP.NET MVC:

  • project structure - there are separate folders for Views, Controllers and Model
  • views placement - by default ASP.NET MVC framework will try to find view for given controller and action in folder like this: /Views/controllername/actionname.aspx.
    Action name equals view name.
As you can see based on ASP.NET MVC example, conventions are not just to eliminate monkey code, they can be also used to establish common standards for project structure, classes/properties naming etc.

Implementation

Lets try to put this high level ideas into concrete ... how Convention over Configuration can be implemented? I will try to present an algorithm based on fluent-nh and real life scenario where we have to map database tables to classes but all tables in database have "tbl" prefix. Of course we don't want to name our classes with this prefix, also we don't want to specify manually for each class that corresponding table's name is "tbl" + class name.

Normally with fluent-nh you would write your own convention implementing IClassConvention interface, then you would use FluentConfiguration class to "fluently" configure your database, add your mappings and also add your custom conventions. Having that you would call BuildSessionFactory() to get instance of properly configured NHibernate's SessionFactory. It's all straightforward but lets look under the hood to see what is going on behind the scene:
  1. In the first step fluent-nh loads classes responsible for "convention discovery". Each class from this group is responsible for single convention, this way it's really simple to add new additional conventions later.
    To find all "discovery" classes you can for instance use reflection to get all classes from a given namespace (flunet-nh's way) or all classes implementing some interface.
  2. Then it instantiates all mapping classes, custom conventions and default conventions. Default conventions will be used if there are no custom conventions. Be default class name will be equal to table name, we want to change it therefore we have to provide our own convention. It's important to remember that default convention shouldn't overwrite custom one.
  3. Next, it executes all "convention discovery" classes. Corresponding methods will get as a parameter all mappings and are responsible for finding and applying specific conventions.
This is high level algorithm which allows you easily add additional conventions. You can have set of default conventions, but also you can provide your custom one. In general it is important to remember that
  1. Any convention (default or custom) shouldn't overwrite explicit configuration!
  2. Default convention shouldn't overwrite custom conventions!

Code samples

All conventions have to implement this simple interface:


   1:  public interface IConvention<T> : IConvention
   2:  {
   3:      /// <summary>
   4:      /// Whether this convention will be applied to the target.
   5:      /// </summary>
   6:      /// <param name="target">Instace that could be supplied</param>
   7:      /// <returns>Apply on this target?</returns>
   8:      bool Accept(T target);
   9:  
  10:      /// <summary>
  11:      /// Apply changes to the target
  12:      /// </summary>
  13:      /// <param name="target">Instance to apply changes to</param>
  14:      void Apply(T target);
  15:  }

Accept() method allows you to have number of conventions of given type and use them based on your custom logic.

This is how "discovery" is implemented:

   1:  public void Apply(IEnumerable<IClassMap> classes)
   2:  {
   3:      var conventions = conventionFinder.Find<IClassConvention>();
   4:  
   5:      foreach (var classMap in classes)
   6:      {
   7:          foreach (var classConvention in conventions)
   8:          {
   9:              if (classConvention.Accept(classMap))
  10:                  classConvention.Apply(classMap);
  11:          }
  12:      }
  13:  }

ConventionFinder returns all objects implementing IClassConvention interface, then it applies this all conventions to all mappings.

Finally, our convention which will add "tbl" prefix for table name:


   1:  public class TableNameConvention : IClassConvention
   2:  {
   3:      public bool Accept(IClassMap target)
   4:      {
   5:          return string.IsNullOrEmpty(target.TableName);
   6:      }
   7:  
   8:      public void Apply(IClassMap target)
   9:      {
  10:          target.WithTable("tbl" + target.EntityType.Name);
  11:      }
  12:  }

And a few interesting pieces of code which I found in fluent-nh sources:


   1:  private void AddDefaultConventions()
   2:  {
   3:      foreach (var foundType in from type in typeof(PersistenceModel).Assembly.GetTypes()
   4:                                where type.Namespace == typeof(TableNameConvention).Namespace && !type.IsAbstract
   5:                                select type)
   6:      {
   7:          ConventionFinder.Add(foundType);
   8:      }
   9:  }

The above method finds all default conventions. Next method instantiates type using default constructor or the one which takes (in this case) ConventionFinder as a parameter:

   1:  private object Instantiate(Type type)
   2:  {
   3:      object instance = null;
   4:  
   5:      foreach (var constructor in type.GetConstructors())
   6:      {
   7:          if (IsFinderConstructor(constructor))
   8:              instance = constructor.Invoke(new[] { this });
   9:          else if (IsParameterlessConstructor(constructor))
  10:              instance = constructor.Invoke(new object[] { });
  11:      }
  12:  
  13:      return instance;
  14:  } 

I hope that this post gives you much better idea about Convention over Configuration in general but also explains how to implement this pattern in practice. I encourage you to give it a try instead of writing monkey code.

If you encountered different cases where CoC fits perfectly I would be very interested to hear about it, leave a comment or send me an email. Or maybe you in general disagree ... leave a comment ... it will be interesting to know your view on this.

Wednesday, 1 April 2009

Conventions - After Rewrite

In December I have written a post about Conventions and AutoPersistenceModel in Fluent NHibernate. Since then lots of things have changed, especially with conventions, in this post I would like to show how to accomplish the same, old goals with new API.

To recap quickly, we have two following tables:

The issues which we are going to solve with conventions include:
AdventureWorks database doesn't follow the default convention. Differences:
  • table name is different then class name (Product vs Production.Product)
  • id property has different name then primary key column (Id vs ProductID)
  • properties representing links between tables have different names then foreign key column names (Product vs ProductID)

Here is how we can create conventions which are specific for AdventureWorks database:

IClassConvention

By default class name equals table name. If for some reason it's not true in your case then basically you have two options:
  • in each mapping class you can call WithTable(...) method and pass as a parameter your custom table name 
  • create your own convention by implementing IClassConvention
Of course second option is preferable in most cases, IClassConvention interface has to methods which need to be implemented:

   1:  public bool Accept(IClassMap target)
   2:  {
   3:      // apply this convention if table wasn't specified with WithTable(..) method
   4:      return string.IsNullOrEmpty(target.TableName);
   5:  }
   6:   
   7:  public void Apply(IClassMap classMap)
   8:  {
   9:      // table name can be like: 
  10:      // "Production." + class name  or  "Sales." + class name
  11:      // "Production" and "Sales" are the last parts of the namespace 
  12:   
  13:      var lastElementOfNamespace = classMap.EntityType.Namespace
  14:          .Substring(classMap.EntityType.Namespace.LastIndexOf('.') + 1);
  15:   
  16:      classMap.WithTable(String.Format("{0}.{1}", lastElementOfNamespace, classMap.EntityType.Name));
  17:  }

I think comments explain everything but I would like to stress the way Accept() method was implemented in. It's important to remeber that we don't always want to apply our conventions! If, for some reason, in mapping class WithTable(...) method was called to set specific table name then we shouldn't overwrite it. 

IIdConvention

Default convention for identity properties is also fairly straightforward - property name equals column name. If you need to change it then again there are two options:
  • in mapping class you can specify column name like this: Id(x => x.Id, "ProductID"); 
  • or implement IIdConvention and provide your own convention

   1:  public bool Accept(IIdentityPart target)
   2:  {
   3:      // make sure that column name wasn't set by calling Id(x => x.Id, "...")
   4:      return string.IsNullOrEmpty(target.GetColumnName());
   5:  }
   6:   
   7:  public void Apply(IIdentityPart target)
   8:  {
   9:      // primary key = class name + "ID"
  10:      target.ColumnName(target.EntityType.Name + "ID");
  11:  }
A few words of explanation, in database, primary keys are named like ProductID, ProductReviewID whereas in our class I want to stay with simple Id property for each class. Hence, to build column name it's required to use class name (for instance Product) and add "ID".

IReferenceConvention and IHasManyConvention

Last issue to deal with are foreign keys. ProductReview table has foreign key to Product table, as usually, property name (Product) is different then column name (ProductID) and therefore instead of specifing that (References(x => x.Product, "ProductID");) we can implement IReferenceConvention interface:


   1:  public bool Accept(IManyToOnePart target)
   2:  {
   3:      // make sure that column name wasn't set with References(x => x.Product, "...");
   4:      return string.IsNullOrEmpty(target.GetColumnName());
   5:  }
   6:   
   7:  public void Apply(IManyToOnePart target)
   8:  {
   9:      // foreign key column name = property name + "ID"
  10:      //
  11:      // it will be used in case like this:
  12:      // <many-to-one name="Product" column="ProductID" />
  13:   
  14:      target.ColumnName(target.Property.Name + "ID");
  15:  }

And to map easily other side of this association, without spcifying key column names we have to implement IHasManyConvention:

   1:  public bool Accept(IOneToManyPart target)
   2:  {
   3:      return target.KeyColumnNames.List().Count == 0;
   4:  }
   5:   
   6:  public void Apply(IOneToManyPart target)
   7:  {
   8:      // foreign key column name = class name + "ID" 
   9:      //
  10:      // it will be used to set key column in example like this:
  11:      //

  12:      // <bag name="ProductReview" inverse="true">
  13:      //   <key column="ProductID" />
  14:      //   <one-to-many class="AdventureWorksPlayground...ProductReview, AdventureWorksPlayground, ..." />
  15:      // </bag>
  16:   
  17:      target.KeyColumnNames.Add(target.EntityType.Name + "ID");
  18:      target.LazyLoad();
  19:      target.Inverse();
  20:  }

Please note that in last example we are not only setting key column name but also some other parameters like lazy load and inverse. 

Last words

As you can see with new API you need to write slightly more lines of code to achieve the same effect but I think it's a fair trade off for much greater control and flexibility. 

This post is just "touching" the subject, you can find much more about conventions and different interfaces on wiki. 

Related posts:

Wednesday, 11 March 2009

Validation of NHibernate Entities

Recently I was reading Billy McCafferty's post "A Few NHibernate Tips" and one point there, related to mapping files, was particularly interesting for me:
Don’t bother including database meta data (e.g., column length) in mapping files unless intending to auto generate the SQL using the hbm2dll tool.
I was really surprised to see that things like column length, information if column accept null values are completely ignored.

Why does it matter?

There are actually two reasons:
  • if details regarding database meta data are irrelevant then why should we write mappings at all? Fluent Nhibernate and Auto Mapping is the way to go.
  • performance impact - it's a huge waste of time to make a call to database just to get an exception saying that some columns don't accept nulls. Luckily, there is a project called NHibernate Validator which can solve this issue.
Fluent NHibernate and NHibernate Validator in one project

It's fairly simple to configure those two to work together:

   1:  public static ISessionFactory GetFluentlyConfiguredSessionFactory()
   2:  {
   3:      return Fluently.Configure()
   4:          .Database(MsSqlConfiguration
   5:                        .MsSql2005
   6:                        .ConnectionString(c => c.Is(ConnectionString)))
   7:          .Mappings(m =>
   8:                    m.FluentMappings.AddFromAssemblyOf<Product>()
   9:                        .ConventionDiscovery.Add(new AdventureWorksConvention())
  10:                        .ExportTo(ExternalMappingsOutputDirectory))
  11:          .ExposeConfiguration(ConfigureNHibernateValidator)
  12:          .BuildSessionFactory();
  13:  }
  14:  
  15:  public static void ConfigureNHibernateValidator(Configuration configuration)
  16:  {
  17:      var nhvc = new NHVConfiguration();
  18:      nhvc.Properties[NHibernate.Validator.Cfg.Environment.ValidatorMode] = "UseAttribute";
  19:      nhvc.Mappings.Add(new NHibernate.Validator.Cfg.MappingConfiguration("AdventureWorksPlayground", null));
  20:      
  21:      var validator = new ValidatorEngine();
  22:      validator.Configure(nhvc);
  23:  
  24:      //Registering of Listeners and DDL-applying here
  25:      ValidatorInitializer.Initialize(configuration, validator);
  26:  }

First method - GetFluentlyConfiguredSessionFactory() is responsible for fluent-nh configuration. Second method, ConfigureNHibernateValidator(), instantiates Validator and registers listeners.
NHibernate Validator has two built-in NHibernate event listeners. Whenever a PreInsertEvent or PreUpdateEvent occurs, the listeners will verify all constraints of the entity instance and throw an exception if any of them are violated. Basically, objects will be checked before any insert and before any update triggered by NHibernate. This includes cascading changes! This is the most convenient and easiest way to activate the validation process. If a constraint is violated, the event will raise a runtime InvalidStateException which contains an array of InvalidValues describing each failure.
In your project you would like to change string "AdventureWorksPlayground", that is a name of your assembly. Probably you don't want to change validation mode which is set to "UseAttribute". Alternative is that you can use XML file for it.

Here is an example of domain object with a few validation rules:

   1:  public class SalesTerritory
   2:  {
   3:      public virtual int Id { get; set; }
   4:      
   5:      [NotNull]
   6:      [Pattern(Regex = "[A-Za-z0-9]+")]
   7:      public virtual string Name { get; set; }
   8:  
   9:      [NotNull]
  10:      public virtual string Group { get; set; }
  11:      
  12:      [Length(Max = 3), NotNull]
  13:      public virtual string CountryRegionCode { get; set; }
  14:  }

Out of the box you can use number different attributes to describe your validation rules, you can also create your own one if necessary. I was running a few tests and the results show that Validator will throw an exception four times faster then the NHibernate itself (and that was with database on the same machine).

You can learn much more about NHibernate Validator on NHibernate Forge wiki.

Shout it

Monday, 9 March 2009

Fluent NHibernate and Inheritance Mapping

While exploring the AdventureWorks database I have found an interesting case where inheritance mapping has to be used. In this post I would like to show how it can be neatly mapped with Fluent NHibernate. Lets start with database schema:

There are a few interesting things about this few tables:
  • There are two types of customers: Store and Individual
  • Each customer is represented as a single record in Customer table and additionally single record in Individual or Store table. Customer has a CustomerType column based on which we can determine the type.
  • Individual and Store tables have a primary key which is also a foreign key to the main Customer table. It means that Customer with ID 1231 will have corresponding record in Store (or Individual) table with the same ID.
Why inheritance is needed here?

The above tables can be also mapped without any fancy inheritance so question is why should we bother? The real reason for this hassle are business rules which are different for each type of customer (and potentially sales territory). To be more specific, there might be different rules for queuing deliveries, calculating discounts etc.

It makes sense to encapsulate those rules in a separate class for each type of consumer. This line of thinking leads us to the class diagram like this:


Customer is marked as a abstract class so it can't be instantiated. For simplicity I have added here only one method GetCurrentDiscount() which represents business logic specific for each type. Additionally StoreConsumer and IndividualConsumer classes have a Details property which "links" to two other tables - Store or Individual.

Inheritance Mapping

In general NHibernate supports three different inheritance mapping strategies. In this case I will use table per class hierarchy strategy. With Fluent NHibernate Customer class can be mapped like this:

   1:  public class CustomerMap : ClassMap<Customer>
   2:  {
   3:      public CustomerMap()
   4:      {
   5:          Id(x => x.Id).GeneratedBy.Native();
   6:  
   7:          Map(x => x.ModifiedDate).Not.Nullable();
   8:          Map(x => x.AccountNumber).ReadOnly().Unique();
   9:  
  10:          References(x => x.SalesTerritory, "TerritoryID");
  11:          
  12:          DiscriminateSubClassesOnColumn<string>("CustomerType")
  13:              .SubClass<IndividualCustomer>("I",
  14:                                            m => m.HasOne(x => x.Details)
  15:                                                     .Cascade.SaveUpdate()
  16:                                                     .FetchType.Join())
  17:              .SubClass<StoreCustomer>("S",
  18:                                       m => m.HasOne(x => x.Details)
  19:                                                .Cascade.SaveUpdate()
  20:                                                .FetchType.Join());
  21:      }
  22:  }
Let me explain what is going on up there:
  • Thanks to Id(...) and Map(...) methods we can tell Fluent NHibernate about identity column and other simple "data" columns. You can find more about basics here.
  • References(...) method is used to map many-to-one association with SalesTerritory table. In this case column name doesn't follow the convention therefore it has to be explicitly specified.
  • CustomerType column is used as a discriminator. "The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row." CustomerType can have two values:
    • I for individual customer, in which case IndividualCustomer class will be instantiated or
    • S for store and for StoreCustomer class.
  • Both subclasses map one additional one-to-one association. Based on above mapping you can't see that actually each subclass "links" to the different table, it will be clear though when you check both classes:
   1:  public class IndividualCustomer : Customer
   2:  {
   3:      public virtual Individual Details { get; set; }
   4:      
   5:      public override double GetCurrentDiscount()
   6:      {
   7:          // some logic here
   8:      }
   9:  }

And StoreCustomer:

   1:  public class StoreCustomer : Customer
   2:  {
   3:      public virtual Store Details { get; set; }
   4:      
   5:      public override double GetCurrentDiscount()
   6:      {
   7:          // some logic here
   8:      }
   9:  }

Please note that Details property has different type in each case, in each case NHibernate will be able to figure out which class should be created and what data should be populated ... awesome!

One-to-one association

There is one additional feature worth mentioning - as it was stated earlier, ID from Customer table equals ID from Store (or Individual) table. How it can be mapped to make sure that inserts will work? I will explain it based on Individual class:

   1:  public class Individual
   2:  {
   3:      public virtual int Id { get; set; }
   4:      public virtual int Contact { get; set; }
   5:      public virtual string Demographics { get; set; }
   6:      public virtual DateTime ModifiedDate { get; set; }
   7:      public virtual IndividualCustomer Customer { get; set; }
   8:  }
Individual class is always associated with individual customer therefore we can explicitly say that property Customer is of type IndividualCustomer. Other properties are fairly straightforward.

Here is the mapping class:

   1:  public class InidividualMap : ClassMap<Individual>
   2:  {
   3:      public InidividualMap()
   4:      {
   5:          Id(x => x.Id, "CustomerID").GeneratedBy.Foreign("Customer");
   6:  
   7:          Map(x => x.Demographics);
   8:          Map(x => x.ModifiedDate);
   9:          Map(x => x.Contact, "ContactID");
  10:  
  11:          HasOne(x => x.Customer).Constrained();
  12:      }
  13:  }

Appropriate generator for identity column is the key here:
foreign - uses the identifier of another associated object. Usually used in conjunction with a one-to-one primary key association.
With above mapping foreign generator knows about the Customer and will use exactly the same ID. More about foreign generator and different types of one-to-one association you can find here.

Related articles:

Shout it

Tuesday, 17 February 2009

FluentConfiguration -- New API to configure NHibernate

Fluent NHibernate from the very beginning provides really clean API to configure NHibernate. I didn't expect to see any changes in this area ... but yet new "fluent" way to configure NHibernate has been introduced.

This is the way I was using so far (it still works well):

   1:  private static Configuration GetNHibernateConfig()
   2:  {
   3:      return MsSqlConfiguration.MsSql2005
   4:          .ConnectionString(c => c.Is(@"Data Source=db_server;Database=db_name;...."))
   5:          .UseReflectionOptimizer()
   6:          .ShowSql()
   7:          .ConfigureProperties(new Configuration());
   8:  }
   9:  
  10:  public static ISessionFactory GetSessionFactory()
  11:  {
  12:      // configure nhibernate
  13:      Configuration config = GetNHibernateConfig();
  14:  
  15:      var models = new PersistenceModel();
  16:      
  17:      // alter default conventions if necessary
  18:      SetUpConvention(models.Conventions);
  19:  
  20:      models.addMappingsFromAssembly(typeof (Product).Assembly);
  21:      models.Configure(config);
  22:  
  23:      // save xml files with mappings to some random location
  24:      models.WriteMappingsTo(@"c:\dev\mappings");
  25:  
  26:      // build factory
  27:      return config.BuildSessionFactory();
  28:  }
  29:  

Honestly I didn't expect that readability can be improved much ... but check this:

   1:  public static ISessionFactory GetFluentlyConfiguredSessionFactory()
   2:  {
   3:      return Fluently.Configure()
   4:          .Database(MsSqlConfiguration
   5:                        .MsSql2005
   6:                        .ConnectionString(c => c.Is(@"Data Source=db_server;Database=db_name;....")))
   7:  
   8:          .Mappings(m =>
   9:                    m.FluentMappings.AddFromAssemblyOf<Product>()
  10:                        .ConventionDiscovery.Add(new AdventureWorksConvention())
  11:                        .ExportTo(@"c:\dev\mappings"))
  12:  
  13:          .BuildSessionFactory();
  14:  }

What I really like about Fluent NHibernate is that it doesn't force user to take all or nothing. If you want you can easily use combine different types of mappings. For instance you can add fluent-nh to your existing project, reuse old mappings (XML files) and add new mappings configured with fluent API. Here is an example:

   1:  public static ISessionFactory GetFluentlyConfiguredSessionFactoryWithHbmFiles()
   2:  {
   3:      return Fluently.Configure()
   4:          .Database(MsSqlConfiguration
   5:                        .MsSql2005
   6:                        .ConnectionString(c =>
   7:                                          c.Is(@"Data Source=db_server;Database=db_name;....")))
   8:  
   9:          .Mappings(m =>
  10:                        {
  11:                            m.FluentMappings.AddFromAssemblyOf<Product>()
  12:                                .ConventionDiscovery.Add(new AdventureWorksConvention())
  13:                                .ExportTo(@"c:\dev\mappings");
  14:                            m.HbmMappings.AddFromAssemblyOf<Product>();
  15:                        })
  16:  
  17:          .BuildSessionFactory();
  18:  }
In a similar way it is possible to combine standard fluent-nh mappings with XML files and auto mapping.

I didn't include in this post anything about conventions to keep things short and concise but you can find details in this post.

Related posts:

Friday, 6 February 2009

Fluent NHibernate and Collections Mapping

You can find some bits and pieces about mapping collections with NHibernate in many different places but yet I decided to write another post about it. What is different about my post? I hope to gather here all (in one place) relevant information regarding the most common mappings: many-to-one/one-to-many and many-to-many. In my examples I'm useing Fluent NHibernate API but also XML counterpart are included. All examples are based on the following schema: (subset of AdventureWorks database)


Bidirectional many-to-one/one-to-many

This is the most common type of association, I have used Product and ProductReview tables to depict how it works and how it can be mapped.

In our case each product (one) can have multiple reviews (many). On ProductReview side association can be mapped like this:

   References(x => x.Product, "ProductID").FetchType.Join();
which is equal to:

   <many-to-one fetch="join" name="Product" column="ProductID" />
What is FetchType doing? Basically FetchType can be Select or Join, we can define how we want NHibernate to load product for us, consider this code:

   1:  var review = session.Get<ProductReview>("1");
   2:  var productName = review.Product.Name;    

For FetchType.Join() NHibernate will call database once with following query:

   SELECT ... FROM ProductReview pr left outer join Product p on pr.ProductID=p.ProductID WHERE ... 
As you can see review and product are loaded with one call. For FetchType.Select() we will get two calls:

   SELECT ... FROM ProductReview pr WHERE ... 
   SELECT ... FROM Product p WHERE ... 
Second call will be executed on demand, it means, only if we try to use Product object like in above example: var productName = review.Product.Name;

In general you have to determine in each case which FetchType is more beneficial for you.

Now, lets check Product side, this is many side of one-to-many association so Product has a collection of reviews, I have chosen to use ISet:

   1:  HasMany(x => x.ProductReview)
   2:      .KeyColumnNames.Add("ProductID")
   3:      .AsSet()
   4:      .Inverse()
   5:      .Cascade.All();
corresponding XML mapping:

   1:      <set name="ProductReview" inverse="true" cascade="all">
   2:        <key column="ProductID" />
   3:        <one-to-many class="AdventureWorksPlayground.Domain.Production.ProductReview, AdventureWorksPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
   4:      </set>
Here you can find two confusing things:
  • inverse="true" - it tells NHibernate that other side of this association is a parent. I know that it sounds other way round but that's how it is. ProductReview table has foreign key (and ProductID column) therefore ProductReview controls the association.
    What are the implications? In above example review.Product has to be set correctly, as this the property which NHibernate will check to figure our what product is associated with the review. It will ignore collection of reviews on product!
  • cascade="all" - it tells NHibernate that all events (like save, update, delete) should be propagated down. Calling session.SaveOrUpdate(product) will save (or update) the product itself but also the same event will be applied to all depending objects.
We are almost ready to move to many-to-many associations, but before we do that, check this piece of code:

   1:  var product = new Product
   2:                    {
   3:                        Name = "Bike",
   4:                        SellStartDate = DateTime.Today
   5:                    };
   6:  
   7:  product.ProductReview.Add(new ProductReview
   8:                                {
   9:                                    Product = product,
  10:                                    Rating = 4,
  11:                                    ReviewerName = "Bob",
  12:                                    ReviewDate = DateTime.Today
  13:                                });
  14:  
  15:  product.ProductReview.Add(new ProductReview
  16:                                {
  17:                                    Product = product,
  18:                                    Rating = 2,
  19:                                    ReviewerName = "John",
  20:                                    ReviewDate = DateTime.Today
  21:                                });
  22:  
  23:  
  24:  session.SaveOrUpdate(product);

Can you see a potential problem here? Each ProductReview knows about its Product, and thanks to cascade="all" everything is configured correctly but still you may end up with just one review in database ... why? I'm using ISet here, so it guarantees that I have only unique objects in the collection. Most of the people know that NHibernate classes should have Equals() and GetHashCode() methods overridden. It is useful when you want to check that two objects represent the same row in a database. People use primary id column in Equals() implementation, primary id is unique so it fits perfectly isn't it? It does if primary key is defined, and in above example, objects are saved in a last line, before that, they don't have any primary id. That is a reason for using different data to determine equality.

Bidirectional many-to-many association

For this association I have used Product, ProductProductPhoto (link) and ProductPhoto tables. Each product can have multiple photos, but each photo can also be associated with multiple products. ProductProductPhoto is just a link table and doesn't have any representation as a separate class. On both sides mapping looks very similarly.

Product side:

   1:  HasManyToMany(x => x.Photos)
   2:      .AsBag()
   3:      .WithTableName("Production.ProductProductPhoto")
   4:      .WithParentKeyColumn("ProductID")
   5:      .WithChildKeyColumn("ProductPhotoID")
   6:      .Cascade.All();
which produces XML like this:

   1:  <bag name="Photos" cascade="all" table="Production.ProductProductPhoto">
   2:        <key column="ProductID" />
   3:        <many-to-many column="ProductPhotoID" class="AdventureWorksPlayground.Domain.Production.ProductPhoto, AdventureWorksPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
   4:  </bag>
and ProductPhoto side:

   1:  HasManyToMany(x => x.Products)
   2:      .AsBag()
   3:      .WithTableName("Production.ProductProductPhoto")
   4:      .WithParentKeyColumn("ProductPhotoID")
   5:      .WithChildKeyColumn("ProductID")
   6:      .Inverse();
XML:

   1:  <bag name="Products" inverse="true" table="Production.ProductProductPhoto">
   2:        <key column="ProductPhotoID" />
   3:        <many-to-many column="ProductID" class="AdventureWorksPlayground.Domain.Production.Product, AdventureWorksPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
   4:  </bag>
In efect Product has a collection of Photos (IList<ProductPhoto> Photos) and ProductPhoto has a collection of products. It's mandatory, in cases like this, to mark one side as inverse="true".

This is fairly straightforward example but unfortunately not very common. In typical case link table has some additional data (like ProductDocument which has ModifiedDate column) and those additional data forces us to use different approach. Among NHibernate best practices you can find general guideline:

Good usecases for a real many-to-many associations are rare. Most of the time you need additional information stored in the "link table". In this case, it is much better to use two one-to-many associations to an intermediate link class. In fact, we think that most associations are one-to-many and many-to-one, you should be careful when using any other association style and ask yourself if it is really necessary.
So, in fact, for tables Product, Document and ProductDocument, we have to create three classes and three mappings. Both Product and Document have a link to each other through ProductDocument object. Interesting part is ProductDocument which has composite primary id (two columns) which can be mapped in a following way:

   1:      public class ProductDocumentMap : ClassMap<ProductDocument>
   2:      {
   3:          public ProductDocumentMap()
   4:          {
   5:              UseCompositeId()
   6:                  .WithKeyReference(x => x.Product, "ProductID")
   7:                  .WithKeyReference(x => x.Document, "DocumentID");
   8:  
   9:              Map(x => x.ModifiedDate).Not.Nullable();
  10:          }
  11:      }
and it generates XML like this:

   1:    <class name="ProductDocument" table="Production.ProductDocument" xmlns="urn:nhibernate-mapping-2.2">
   2:      <composite-id>
   3:        <key-many-to-one class="AdventureWorksPlayground.Domain.Production.Product, AdventureWorksPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Product" column="ProductID" />
   4:        <key-many-to-one class="AdventureWorksPlayground.Domain.Production.Document, AdventureWorksPlayground, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Document" column="DocumentID" />
   5:      </composite-id>
   6:      <property name="ModifiedDate" column="ModifiedDate" not-null="true" type="DateTime">
   7:        <column name="ModifiedDate" />
   8:      </property>
   9:    </class>
Then we can write code like this:

   1:      var product = CreateNewProduct();
   2:      var photo1 = CreateNewPhoto();
   3:      var photo2 = CreateNewPhoto();
   4:  
   5:      product.Photos.Add(photo1);
   6:      product.Photos.Add(photo2);
   7:  
   8:      // we don't have to save photos because of Cascade.SaveUpdate()
   9:      // INSERT INTO [Production.Product]
  10:      // INSERT INTO [Production.ProductPhoto]
  11:      // INSERT INTO [Production.ProductPhoto]
  12:      // INSERT INTO [Production.ProductProductPhoto]
  13:      // INSERT INTO [Production.ProductProductPhoto]
  14:      session.SaveOrUpdate(product);
And that is all what I think is important in this subject ... anything missing? Leave a comment and I will try to add missing parts.

Related posts

Wednesday, 17 December 2008

Fluent NHibernate - Integration Tests

In last two posts I have covered the Flunet NHibernate introduction and Coneventions together with AutoPersistenceModel. In this part I would like to show how Fluent NHibernate can speed up writing integration tests which should guarantee that mappings are correct. If there is a problem with mappings then for sure that is something you would like to want know about as fast as possible therefore some sort of automated tests are necessary.

Lets check what Fluent NHibernate has to offer:

1) SessionSource class which helps to deal with NHibernate configuration, session management and recreating database schema. Why is it useful? "Just" to check if mappings are correct you don't need to have any test data, you need only clear schema and database to connect with. You can use SQLite to create in-memory database. This is a neat and robust solution and moreover it eliminates external dependency -- we don't need any external database any more. Check the example:



   1:  [SetUp]
   2:  public void SetUp()
   3:  {
   4:      // create and configure persistance model including changes in Conventions
   5:      var persistenceModel = new PersistenceModel();
   6:      persistenceModel.Conventions.GetTableName =
   7:          type =>
   8:          String.Format("[{0}.{1}]", type.Namespace.Substring(type.Namespace.LastIndexOf('.') + 1), type.Name);
   9:      persistenceModel.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
  10:      persistenceModel.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
  11:      persistenceModel.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
  12:  
  13:      // add mappings
  14:      persistenceModel.addMappingsFromAssembly(typeof (Product).Assembly);
  15:  
  16:      // configure nhibernate using SQLite database
  17:      var config = new SQLiteConfiguration()
  18:          .InMemory()
  19:          .ShowSql();
  20:  
  21:      sessionSource = new SessionSource(config.ToProperties(), persistenceModel);
  22:  
  23:      // create NHibernate session
  24:      session = sessionSource.CreateSession();
  25:  
  26:      // recreate schema
  27:      sessionSource.BuildSchema(session);
  28:  }


Here is an implementation of BuildSchema(...) method:



   1:  public void BuildSchema(ISession session)
   2:  {
   3:      IDbConnection connection = session.Connection;
   4:  
   5:      string[] drops = _configuration.GenerateDropSchemaScript(_dialect);
   6:      executeScripts(drops, connection);
   7:  
   8:      string[] scripts = _configuration.GenerateSchemaCreationScript(_dialect);
   9:      executeScripts(scripts, connection);
  10:  }


As you can see, it uses NHibernate methods to generate DDL for dropping and creating tables for selected dialect (SQLite in this case). One thing worth noticing is that DDLs will be as accurate as mappings. So if you skip some information (like nullable fields) then don't expect to see it there!

2) Thanks to the SessionSource class we can query in-memory database and we have access to NHibernate Session ... it's time to check our mappings, we can use PersistenceSpecification class to do that:



   1:  [Test]
   2:  public void ProductReviewTest()
   3:  {
   4:      new PersistenceSpecification<ProductReview>(session)
   5:          .CheckProperty(x => x.Comments, "some nice comment")
   6:          .CheckProperty(x => x.EmailAddress, "test@test.com")
   7:          .CheckProperty(x => x.ModifiedDate, DateTime.Today)
   8:          .CheckProperty(x => x.Rating, 4)
   9:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  10:          .CheckProperty(x => x.ReviewDate, DateTime.Today)
  11:          .CheckProperty(x => x.ReviewerName, "test name")
  12:          .CheckReference(x => x.Product, CreateNewProduct())
  13:          .VerifyTheMappings();
  14:  }

Under the hood PersistenceSpecification class will save the object (ProductReview) to the database and then using another connection it will fetch the object back to make sure that all properties have correct values set. It's not a revolution but for sure it can save lots of time and thanks to neat and readable code it will increase maintainability.

Source Code

As always you can download the source code, and the whole sample web application. You will find there separate project for tests. I have created an AbstractTestBase class which is responsible for configuration and it also exposes NHibernate session. There are also tests for mappings, which use exposed NHibernate session and at least on my machine all tests pass ;)


(EDIT: Examples in this post have been updated on 8.02.2009 to reflect changes in Fluent NHibernate API)

Useful Links:
Other related posts:

Monday, 15 December 2008

Fluent NHibernate - Conventions and AutoPersistenceModel

Last time I have introduced the Fluent NHibernate, this time I would like to move further and show you how to use Conventions and AutoPersistenceModel.

But before we move to the point lets take a quick look on things that have changed in our sample web application:

1) Global.asax

NHibernate SessionFactory is created only once, on application start, then it is reused to create Sessions for each request. This approach allows us to use NHibernate Session in code behind of ASP.NET pages.

2) To demonstrate more interesting things I had to add one more table, so now we are playing with two tables:


I am, of course, still using AdventureWorks database. With a new table and relations between those two tables, our mappings have changed:

  • Product:


   1:  public class ProductMap : ClassMap<Product>
   2:  {
   3:      public ProductMap()
   4:      {
   5:          WithTable("Production.Product");        
   6:          Id(x => x.Id, "ProductID");
   7:   
   8:          Map(x => x.SellEndDate);
   9:          .
  10:          .
  11:          .
  12:          Map(x => x.ModifiedDate).Not.Nullable();
  13:   
  14:          HasMany<ProductReview>(x => x.ProductReview).WithKeyColumn("ProductID").AsBag().Inverse();
  15:      }
  16:  }

  • ProductReview:



   1:  public class ProductReviewMap : ClassMap<ProductReview>
   2:  {
   3:      public ProductReviewMap ()
   4:      {
   5:          WithTable("Production.ProductReview");
   6:   
   7:          Id(x => x.Id, "ProductReviewID");
   8:   
   9:          Map(x => x.Comments).WithLengthOf(3850);
  10:          Map(x => x.EmailAddress).Not.Nullable().WithLengthOf(50);
  11:          Map(x => x.Rating).Not.Nullable();
  12:          Map(x => x.ReviewDate).Not.Nullable();
  13:          Map(x => x.ReviewerName).Not.Nullable().WithLengthOf(50);
  14:          Map(x => x.ModifiedDate).Not.Nullable();
  15:   
  16:          References(x => x.Product, "ProductID");
  17:      }
  18:  }


Please note that I have skipped some irrelevant parts of the mappings but you can download the sample project to get source code. A bit of clarification:
  • Product can have 0 or multiple reviews. From the code point of view, Product class has additional IList property.
  • Review is about a product, therefore there is a not null foreign key in the ProductReview table.


Conventions

Now we can move to the point ... as you can see in above mappings, in some places it's required to specify column name or table name. This is because AdventureWorks database doesn't follow the default convention. (check Convention over Configuration design pattern) Differences:
  • table name is different then class name (Product vs Production.Product)
  • id property has different name then primary key column (Id vs ProductID)
  • properties representing links between tables have different names then foreign key column names (Product vs ProductID)
Luckily for us we don't have to repeat the same changes for all our mappings ... we can change the default convention ... here is how it can be done:



   1:  var models = new PersistenceModel();
   2:   
   3:  // table name = "Production." + class name
   4:  models.Conventions.GetTableName = type => String.Format("{0}.{1}", "Production", type.Name);
   5:   
   6:  // primary key = class name + "ID"
   7:  models.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
   8:   
   9:  // foreign key column name = class name + "ID" 
  10:  //
  11:  // it will be used to set key column in example like this:
  12:  //
  13:  // <bag name="ProductReview" inverse="true">
  14:  //   <key column="ProductID" />
  15:  //   <one-to-many class="AdventureWorksPlayground...ProductReview, AdventureWorksPlayground, ..." />
  16:  // </bag>
  17:  models.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
  18:   
  19:  // foreign key column name = property name + "ID"
  20:  //
  21:  // it will be used in case like this:
  22:  // <many-to-one name="Product" column="ProductID" />
  23:  models.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
  24:   
  25:  models.addMappingsFromAssembly(typeof(Product).Assembly);
  26:  models.Configure(config); 


and our mapping can be simplified to this:



   1:  public class ProductReviewMap : ClassMap<ProductReview>
   2:  {
   3:      public ProductReviewMap()
   4:      {
   5:          Id(x => x.Id);
   6:   
   7:          Map(x => x.Comments).WithLengthOf(3850);
   8:          Map(x => x.EmailAddress).Not.Nullable().WithLengthOf(50);
   9:          Map(x => x.Rating).Not.Nullable();
  10:          Map(x => x.ReviewDate).Not.Nullable();
  11:          Map(x => x.ReviewerName).Not.Nullable().WithLengthOf(50);
  12:          Map(x => x.ModifiedDate).Not.Nullable();
  13:   
  14:          References(x => x.Product);
  15:      }
  16:  }

EDIT: API for conventions was changed completely therefore code which is above is no longer valid, you can find update in this post - Conventions After Rewrite


AutoPersistenceModel

In fact ... in our mappings, there is not much left ... for sure you won't find there anything particularly creative therefore why not get rid of it completely? Yes ... it's possible ... if everything is 100% in accordance with the convention then you can simply use the following code to get NHibernate configured:



   1:  var models = AutoPersistenceModel
   2:      .MapEntitiesFromAssemblyOf<ProductReview>()
   3:      .Where(t => t.Namespace == "AdventureWorksPlayground.Domain.Production" );
   4:   
   5:  models.Conventions.GetTableName = prop => String.Format("{0}.{1}", "Production", prop.Name);
   6:  models.Conventions.GetPrimaryKeyNameFromType = type => type.Name + "ID";
   7:  models.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
   8:  models.Conventions.GetForeignKeyName = prop => prop.Name + "ID";
   9:   
  10:  models.Configure(config);


And that is all what you need ... a few POCO objects representing database tables, AutoPersistenceModel and you are ready to go. For sure it allows you to start very fast with development but what worries me is that there is no way to say that some properties are mandatory or have length limit. Specifying those additional data may help you to discover data related problems faster and moreover, it should increase performance of the NHibernate ... but is it worth it? What do YOU think?

(EDIT: Examples in this post have been updated on 6.02.2009 to reflect changes in Fluent NHibernate API)

Links:
Other interesting posts about Conventions and AutoPersistenceModel: