MonoRail provides a custom databinding solution for ActiveRecord, but there is nothing currently for NHibernate. One of the difficulties in creating an implementation for NHibernate is that there is no standard place to look for the ISession and no required base classes or interface that would identify a persistable object. Each application developer is free to solve these problems as they see fit for their project. Nevertheless, persistance aware databinding makes development with MonoRail much simpler. Below are two base classes that will help in implementing this functionality for NHibernate. All you need to do is inherit from them and implement the abstract members. These classes were based on the current ActiveRecord implementation and some work on my present MonoRail project. They have met our databinding needs, but they may require extension to handle more advanced scenarios.
using System; using System.Collections.Generic; using Castle.Components.Binder; using Iesi.Collections.Generic; using System.Collections; namespace Castle.MonoRail.NHibernateSupport { public abstract class NHDataBinderBase : DataBinder { /// <summary> /// Retrieves the given type with the given key from the persistance store. /// </summary> /// <param name="instanceType"></param> /// <param name="key"></param> /// <returns></returns> protected abstract object GetInstance(Type instanceType, object key); /// <summary> /// Determines the name of the primary key property on this type. /// </summary> /// <param name="instanceType"></param> /// <returns></returns> protected abstract string GetPrimaryKeyName(Type instanceType); /// <summary> /// Determines whether the type is one that would be stored by NHibernate. /// </summary> /// <param name="instanceType"></param> /// <returns></returns> protected abstract bool IsPersistable(Type instanceType); /// <summary> /// Return true if the object has not previously been persisted. /// </summary> /// <param name="instance"></param> /// <returns></returns> protected abstract bool IsTransient(object instance); protected override object CreateInstance(Type instanceType, string paramPrefix, Node node) { if (node == null) throw new BindingException( "Nothing found for the given prefix. Are you sure the form fields are using the prefix " + paramPrefix + "?"); if (node.NodeType != NodeType.Composite) throw new BindingException("Unexpected node type. Expecting Composite, found " + node.NodeType); object instance; if(IsPersistable(instanceType)) { CompositeNode root = (CompositeNode)node; Node childNode = root.GetChildNode(GetPrimaryKeyName(instanceType)); if (childNode != null && childNode.NodeType != NodeType.Leaf) instance = base.CreateInstance(instanceType, paramPrefix, node); else { LeafNode leaf = (LeafNode)childNode; if (leaf == null) instance = base.CreateInstance(instanceType, paramPrefix, node); else { instance = GetInstance(instanceType, leaf.Value); if(instance == null) instance = base.CreateInstance(instanceType, paramPrefix, node); } } } else instance = base.CreateInstance(instanceType, paramPrefix, node); return instance; } protected override bool ShouldRecreateInstance(object value, Type type, string prefix, Node node) { if (IsContainerType(type)) return true; else if (node != null && IsPersistable(type)) { if (IsTransient(value)) return true; else { CompositeNode root = (CompositeNode)node; Node childNode = root.GetChildNode(GetPrimaryKeyName(type)); LeafNode leaf = (LeafNode)childNode; return leaf != null; } } else return base.ShouldRecreateInstance(value, type, prefix, node); } private static bool IsContainerType(Type type) { bool isContainerType = type == typeof(IList) || type == typeof(ISet<>); if (!isContainerType && type.IsGenericType) { Type[] genericArgs = type.GetGenericArguments(); Type genType = typeof(ICollection<>).MakeGenericType(genericArgs); isContainerType = genType.IsAssignableFrom(type); } return isContainerType; } } }
using System; using System.Reflection; using Castle.Components.Binder; using Castle.MonoRail.Framework; namespace Castle.MonoRail.NHibernateSupport { [AttributeUsage(AttributeTargets.Parameter), Serializable] public abstract class NHDataBindBaseAttribute : DataBindAttribute, IParameterBinder { public NHDataBindBaseAttribute(string prefix) : base(prefix) {} /// <summary> /// Creates an instance of your custom data binder. /// </summary> /// <returns></returns> protected abstract NHDataBinderBase CreateDataBinder(); public override object Bind(SmartDispatcherController controller, ParameterInfo parameterInfo) { NHDataBinderBase binderBase = controller.Binder as NHDataBinderBase; if (binderBase == null) binderBase = CreateDataBinder(); CompositeNode treeRoot = controller.ObtainParamsNode(From); object instance = binderBase.BindObject(parameterInfo.ParameterType, Prefix, Exclude, Allow, treeRoot); BindInstanceErrors(controller, binderBase, instance); PopulateValidatorErrorSummary(controller, binderBase, instance); return instance; } } }
Once you have implemented the abstract members, just use your attribute on method parameters and make sure to pass an instance of your databinder into the constructor of the Controller class.

This is fantastic, though the one thing I wonder is that is there anything that ties this to NH? If not then it could renamed to something more appropriate...
Thanks very much for this!