In the comments of my previous post - Descriptive State Enumeration - Maxim Tihobrazov asked me to show how to map a state pattern with NHibernate; and I am more than happy to oblige!
I certainly don't claim to be an expert on NHibernate, but I do use it on a daily basis and I've solved my fair share of problems - including how to map a State pattern with NHibernate. According the NHibernate inheritance documentation, there are three core ways of mapping this pattern:
In my current set of applications, I've always used the table per class hierarchy option so that is what I will describe here. My reasons for choosing this option are simplicity in the mapping and more importantly - not having a need to store the records in their own tables. Each of my states can be very cleanly represented as a record in my state table.
Let's consider the same coffee shop that I've used in both of my PTOM posts - in this case, we're dealing specifically with the Order class and it's OrderStatus state. It's necessary to note that when I am working with NHibernate and the state pattern, I actually do set up my state models a little different. First off - we need an Id for NHibernate to map to the table's primary key. Secondly, I like to keep the core information in the abstract base class and provide all the varying values via a constructor. Third, each of the inheriting classes needs to provide a default (no args) constructor. Forth, we need a way for NHibernate to know which class it should actually instantiate, when loading - a "descriminator". I like to use a simple string Name property for this. And lastly - I think (though I am likely wrong on this aspect) that the abstract base class needs to provide a default constructor. Fortunately, the constructors we need don't need to be public - they can be private.
With all of that being said, here is realistic code base that I would use for my Order and OrderStatus model:
public class Order
{
public int Id { get; set; }
public OrderStatus Status { get; set; }
}
public abstract class OrderStatus
public static InProcess = new InProcessStatus();
public static Totaled = new TotaledStatus();
public static Tendered = new TenderedStatus();
public static Delivered = new DeliveredStatus();
public bool DisplayForFullfillment { get; }
public string Name { get; set; }
private OrderStatus() { }
private OrderStatus(int id, bool display, string name)
Id = id;
DisplayForFullfillment = display;
Name = name;
public class InProcessStatus: OrderStatus
private InProcessStatus(): base(1, false, "InProcess");
public class TotaledStatus: OrderStatus
private TotaledStatus(): base(2, true, "Totaled");
public class TenderedStatus: OrderStatus
private TenderedStatus(): base(3, true, "Tendered");
public class DeliveredStatus: OrderStatus
private DeliveredStatus(): base(4, false, "Delivered");
I'm a huge fan of Fluent NHibernate. I've been using it since just after it was branched from the original ShadeTree code. And at one point, I had submitted so many patches that I was made a comitter on the project. So, it should be no surprise to anyone that I'm going to advocate using FluentNHibernate over the .hbm.xml mapping files. Truth be told, I don't even remember how to do the needed descriminators in hbm.xml files. There was some trick to the descriminator being the first specified items after the Id or something... it's just easier to do in FluentNHibernate, so I don't bother with hbm.xml files anymore.
The Order map is going to be as standard as any other map. You don't need to do anything special here. Just map the Id of the Order and the referenced OrderStatus. (I like to add a "CreateMap()" method that is called from the constructor, so that I can avoid the "virtual method call from a constructor" warning. But I also use the "treat warnings as errors" option for my C# projects).
public class OrderMap: ClassMap<Order>
public OrderMap()
CreateMap();
public void CreateMap()
DefaultAccess.AsProperty();
WithTable("Orders");
Id(o => o.Id).GeneratedBy.Assigned();
It's really the OrderStatus map that is special in this case. This is where we get into the details of the descriminator - telling NHibernate which specific instance to create, when loading the data from the database. In our case, we have added a "Name" field to our OrderStatus object and we will be explicitly using this property as the descriminator.
public class OrderStatusMap: ClassMap<OrderStatus>
public OrderStatusMap()
WithTable("OrderStates");
Id(s => s.Id).GeneratedBy.Assigned();
DiscriminateSubClassesOnColumn<string>("Name")
.SubClass<InProcessStatus>()
.IsIdentifiedBy(OrderStatus.InProcess.Name)
.MapSubClassColumns(x => { })
.SubClass<TotaledStatus>()
.IsIdentifiedBy(OrderStatus.Totaled.Name)
.SubClass<TenderedStatus>()
.IsIdentifiedBy(OrderStatus.Tendered.Name)
.SubClass<DeliveredStatus>()
.IsIdentifiedBy(OrderStatus.Delivered.Name)
Map(s => s.Name);
The key to all of this is the DescriminateSubClassesOnColumn method. The generics <string> tells us what .NET Type is being used to identify the specific class instances that we are dealing with and the ("Name") parameter is the column name in the database that represents the specific instance.
Then, we have the specific class instances referenced by the ".Subclass<type>" calls. This is a fluent interface, so ".Subclass" is a method that gets called directly off the DescriminatoeSubClassesOnColumn method. The specific type we want NHibernate to know about is specified in the generics parameter: ".SubClass<InProcessStatus>". The "IsIdentifiedBy" is where we give NHibernate the knowledge of what value in the "Name" column maps to what specific class in our code. To prevent magic string syndrome from setting in, I like to use the actual enumeration pattern of my OrderStatus to specify the string name of the class.
And finally, we have to provide an ugly workaround for our maps via the "MapSubclassColumns" method. There is an implementation issue in FluentNHibernate currently, and because of this issue we are forced to call the "MapSubclassColumns" method, with an empty lambda expression. If we don't call this method, the sub class will not get registered and NHibernate will not know how to handle the data in question. (I am hoping to fix this issue at some time, and make it so we don't have to call that method. I just haven't had time recently.)
The rest of the map (the one remaining "name" property being mapped, in this case) is a regular NHibernate map. You'll notice, though, that we never mapped the "DispalyForFullfillment" property. I explicitly choose to leave any and all "volatile" properties out of my state maps. By doing this, I am able to add, edit, and remove any properties or methods on the state objects that I need, without having to change the NHibernate mappings. Since the state objects I am dealing with don't change without recompiling the code anyway, I don't need the ability to define them in the database. However, if you do need or want the flexibility of defining your states in the database, you can map each individual property of the state. Just remember that you will have to modify both the code and the database, to make the changes complete.
I hope this quick look at mapping a state pattern with Fluent NHibernate will help to shed some light on the subject, for someone. I realize that I have only provided the FluentNHibernate mapping, though. I specifically chose to do this because it would be a serious chore for myself to create a code project with NHibernate set up so that I can actually verify my mapping xml is correct. However, the translation from FluentNHibernate back to standard .hbm.xml files should be fairly straightforward, with the help of the NHibernate Documentation. If anyone out there is willing to help out and send me the correct .hbm.xml mapping, I would be more than happy to add it to this post and credit you with the work.
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.