State Pattern, Enumeration Class and Fluent NHibernate (Oh my!)

November 1st, 2009

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!


  • http://blog.cromwellhaus.com Ryan Cromwell

    Richard, Would you be willing to post a sample project showing what you’ve done. I’m not sure I follow your final MyStatus implementation.

    • http://elegantcode.com Richard Cirerol

      Ryan, I’m sure I could post something. What are you not following? The implementation of the state pattern? Or the implementation of FNH? Or …?

  • Don Cote

    Thanks a lot for the write up. My one sticking point so far is on how to handle this when I have a class that holds a collection of these Enumeration types.

    For instance…

    public virtual IList MyStatusList
    {
    get{ return ???? }
    set{ _statusList = ???? }
    }

    Any ideas on how to map these using Fluent NHibernate?

  • http://elegantcode.com Richard Cirerol

    Don,

    I am curious to know why you would return a list of statuses on one instance. But, I guess I would serialize the values to a delimited string if needed. Something along the lines of this:

    protected string _statusList;
    public IList StatusList
    {
    get
    {
    var list = new List
    ();
    _statusList.Split(new[]{“;”},StringSplitOptions.None).ToList()
    .ForEach(x=>list.Add(Enumeration.FromValue
    (x));
    return new List
    {list};
    }
    set{ _statusList=string.Join(“;”,value.Select(x=>x.Value).ToArray());}
    }

    and the fluent mapping would be:

    public void Override(AutoMapping mapping)
    {
    mapping.Map(x => x.StatusList)
    .CustomType(typeof(string))
    .Access.CamelCaseField(Prefix.Underscore);
    }

    You could also serialize to JSON or XML before persisting the value. The idea is that the protected member is what you would store, while the public member is what you want to interact with.

    Hope that helps.

  • http://blog.cromwellhaus.com Ryan Cromwell

    @Richard Cirerol
    My sticking point is with the FNH. Do you use the property backing field for ease rather than a custom UserType?

  • http://elegantcode.com Richard Cirerol

    We are using a custom user type for regular enums, but this Enumeration did not seem to conform to the user type conventions. Can you post a sample of a custom user type to encapsulate this problem? I would be very interested.

  • Don Cote

    Richard Cirerol :
    Don,
    I am curious to know why you would return a list of statuses on one instance.

    My particular situation has to do with business units being allowed to use different invoicing event types — just stuck w/ the Status example from above.

    By the way – thanks for the help! Your code example worked out great.

  • Pingback: Migrating to Fluent NHibernate - Jimmy Bogard - Los Techies : Blogs about software and anything tech!