State Pattern, Enumeration Class and Fluent NHibernate (Oh my!)
Recently, I needed to change a basic enumeration into a full-fledged state pattern. After getting all my domain classes updated, I began reviewing the persistence layer. And I hit a wall. I wasn’t sure how I wanted to update my Fluent NHibernate convention to persist the current state.
My original classes were similar to this:
public class MyProgress{
public virtual Guid Id { get; set; }
...
...
public virtual MyStatus Status { get; private set; }
}
public enum MyStatus{
New,
InProgress,
Completed,
Canceled,
Failed
}
My new state pattern was similar to this:
public abstract class MyStatus
{
public static readonly MyStatus New = new NewStatus();
public static readonly MyStatus InProgress = new InProgressStatus();
...
}
public class NewStatus: MyStatus
{
public override void Start(MyProgress progress)
{
progress.SetStatus(InProgress);
}
public override void Cancel(MyProgress progress)
{
progress.SetStatus(Cancelled);
}
public override void Fail(MyProgress progress)
{
progress.SetStatus(Failed);
}
}
...
Here is the problem: My enumeration was persisted as an integer field on the record. Now that I had a state pattern, how should I save my state? I googled the problem and found Derick Bailey’s article on the state pattern and Fluent NHibernate. Derick’s pattern works well, but I felt that creating a lookup table in my database just so I can persist a value in another table was not the path I wanted to traverse – I wasn’t persisting an entity, I was persisting a state value on an entity. Not finding any more love from Google, I asked around and was advised to contact fellow Elegant Coder and Guild3 member, Jason Grundy. Here is Jason’s advice:
“I’ve recently changed from using Enums to an approach outlined by Jimmy Bogard here. In the Entity I would do something like this:
protected int _orderStatusId;
public virtual OrderStatus OrderStatus
{
get { return Enumeration.FromValue<OrderStatus>(_orderStatusId); }
set { _orderStatusId = value.Value; }
}
Then in Fluent NH you can simply map to the protected member.”
I decided to pursue Jason’s advice. The result is my database tables did not need to change at all. Here is some snippets of the updated classes:
public class MyProgress
{
public virtual Guid Id {get;set;}
...
protected int _status;
public virtual MyStatus
{
get{ return Enumeration.FromValue<MyStatus>(_status); }
set{ _status = value.Value; }
}
...
}
public class MyStatus : Enumeration
{
public static MyStatus New = new NewStatus();
...
private class NewStatus : MyStatus
{
public NewStatus() : base (0,"New"){}
public override void Start(MyProgress progress)
{
progress.SetStatus(InProgress);
}
}
...
Notice that the MyStatus class is no longer abstract. However, the subclasses are all private nested classes, each with a value and a display name. I set each subclass value to match the enumeration value it replaced. I did not need to convert my existing data to a new schema.
And here is the Fluent NHibernate override:
public class MyProgressOverride : IAutoMappingOverride<MyProgress>
{
public void Override(AutoMapping<MyProgress> mapping)
{
mapping.Map(x => x.Status)
.CustomType(typeof(int))
.Access.CamelCaseField(Prefix.Underscore);
}
}
As you can see, we use convention-based automapping with overrides, but this same map could be used in a standard class map.
Thanks to Jimmy, Derick, and Jason for the inspiration and assistance!