A question came up on the NHibernate forum that I think is very common. I am sure there is a better way of dealing with this, but I haven't figured it out yet.
I have the following abstraction for a class hierarchy:
public interface IFoo
{
string MyProperty{get;set;}
string MyPropertyWithNoSetter{get;}
}
public interface IFooBar : IFoo{}
public interface IFoosBall : IFoo{}
An abstract class might emerge through refactoring and the concrete implementations of my contracts might end up looking like this:
public abstract class FooBase : IFoo
{
string MyProperty{get;set;}
string MyPropertyWithNoSetter{get;}
}
public class FooBar : FooBase, IFooBar{}
public class FoosBall : FooBase, IFoosBall{}
Now I really want to use IRepository<IFoo> or IRepository<IFooBar> or IRepository<IFoosBall> to interact with all my Foos. But how do I map this in NHibernate so that it will know what I want to do?
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyCore" >
<class name="IFoo" table="Foo" >
<id name="Id" access="property" unsaved-value="0" >
<column name="Id"/>
<generator class="native" />
</id>
<discriminator>
<column name="TypeCode" not-null="true" sql-type="nvarchar(25)" />
</discriminator>
<property name="MyProperty" />
<subclass name="IFooBar" discriminator-value="IFooBar">
<subclass name="FooBar" discriminator-value="FooBar">
<!--note this is repeated from FooBase class-->
<property name="MyPropertyWithNoSetter" access="nosetter.camelcase-underscore"/>
</subclass>
</subclass>
<subclass name="IFoosBall" discriminator-value="IFoosBall">
<subclass name="FoosBall" discriminator-value="FoosBall">
<!--note this is repeated from FooBase class-->
<property name="MyPropertyWithNoSetter" access="nosetter.camelcase-underscore"/>
</subclass>
</subclass>
</class>
</hibernate-mapping>
There are a couple of points of interest here:
- Since interfaces do not have fields, prefer the default 'property' access strategy for NHibernate and provide the getter/setter on properties.
- If you have a property that you must enforce without a setter (see MyPropertyWithNoSetter) and that is represented in a base class (FooBase), then you will need to duplicate the mappings on those properties for the implementations of that base class and set the appropriate access strategy for those properties.
- You MUST provide discriminator values for even those <subclass> elements that will not be persisted (IFooBar and IFoosBall in this case). Otherwise, you'll get an exception telling you that NHibby couldn't instantiate an abstract class or interface. When Nhibernate does a query using your mapping it will use the IN sql operator in the WHERE clause.So an ICriteria using typeof(IFooBar) will have 'WHERE TypeCode IN ('IFooBar','FooBar')' in its query.
Now I will be able to do things like IRepository<IFooBar>().Get(myID) or IRepository<IFoosBall>().FindAll(DetachedCriteria.For(typeof(IFoosBall))...).
Hope this helps...