I came up with two approaches to this.
The first was to add code to my NHibernate Repository class to translate the simple POCO type used by the MVC controller (SimpleThing) to the type of entity that NHibernate wanted (RealThing):
/// <summary>
/// A NHibernate generic repository. Provides base of common
/// methods to retrieve and update data.
/// </summary>
/// <typeparam name="T">The base type to expose
/// repository methods for.</typeparam>
/// <typeparam name="K">The concrete type used by NHibernate</typeparam>
public class NHRepositoryBase<T, K>
: IRepository<T>
where T : class
where K : T, new()
{
// repository methods ...
/// <summary>
/// Return T item as a type of K, converting it if necessary
/// </summary>
protected static K GetKnownEntity(T item) {
if (typeof(T) != typeof(K)) {
K knownEntity = new K();
foreach (var prop in typeof(T).GetProperties()) {
object value = prop.GetValue(item, null);
prop.SetValue(knownEntity, value, null);
}
return knownEntity;
} else {
return (K)item;
}
}
So, any method in the repository can call GetKnownEntity(T item) and it will copy the properties of the item you pass in to the type that NHibernate wants. Obviously this felt a bit clunky, so I looked in to custom model binders.
In the second approach, I created a custom model binder like this:
public class FactoryModelBinder<T>
: DefaultModelBinder
where T : new()
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext,
Type modelType) {
return new T();
}
}
Then I registered that in Global.asax.cs with:
ModelBinders.Binders.Add(typeof(IThing),
new FactoryModelBinder<RealThing>());
And it works fine with a Controller Action that looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NewThing([Bind(Exclude = "Id")] IThing thing) {
// code to process the thing goes here
}
I like the second approach, but most of my dependency injection stuff is in the Controller class. I don't like to have to add all these ModelBinder mappings in Global.asax.cs.