Encapsulation is a key concern in object oriented development. Sometimes you have to break encapsulation a bit to make your code interoperable with the infrastructure. In this article I explain how to make a domain entity play well with NHibernate while keeping an encapsulated interface towards the user.
A brief introduction to access methods in NHibernate
Normally I prefer to map fields to database fields rather than properties. This allows me to put logic into the getter and/or setter of the properties if required while persisting the "raw" state of the fields to and from the database. To map fields instead of properties use the access attribute on the respective property element in the mapping file. You can also define the default access method by setting the default-access attribute on the class element. My mapping files generally look like this
1: <?xml version="1.0" encoding="utf-8" ?>
2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3: assembly="TheAssembly"
4: namespace="SomeNamespace"
5: default-access="field.camelcase"
6: default-lazy="true">
7:
8: <class name="TheClass" proxy="ITheClassProxyInterface">
9: <id name="ID" access="property">
10: <generator class="native"/>
11: </id>
12:
13: <property name="SomeProperty"/>
14: <!-- etc -->
15: </class>
16: </hibernate-mapping>
In line 5 I define the default access to be "fields with camel case naming". This means that a property in the mapping file named "SomeProperty" (line 13) will be mapped to a field named someProperty.
You might have noticed that I explicitly set the access strategy to "property" for the ID. This is necessary when you use proxy interfaces. NHibernate needs to be able to access the ID property even if only a proxy is available. If you try to use field access for the ID property you will most likely get an error message like this:
Could not find field 'id' in class 'DessinDB.Process.IDesign'
So we only have to set access to property and everything is cool? Almost.
The dilemma
Let's start with some code:
1: /// <summary>
2: /// Proxy interface
3: /// </summary>
4: public interface ITestEntity {
5: int ID { get; }
6: }
7:
8: /// <summary>
9: /// Entity class
10: /// </summary>
11: public class TestEntity : ITestEntity {
12: private int id;
13:
14: public int ID {
15: get { return id; }
16: }
17: }
And here's the mapping file:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3: assembly="NTest"
4: namespace="NHTest"
5: default-access="field.camelcase"
6: default-lazy="true">
7:
8: <class name="TestEntity" proxy="ITestEntity">
9: <id name="ID" access="property" >
10: <generator class="native"/>
11: </id>
12:
13: </class>
14: </hibernate-mapping>
Really simple, still it won't work: NHibernate complains that it cannot find the setter for ID.
Could not find a setter for property 'ID' in class 'NHTest.TestEntity'
Adding a setter to TestEntity.ID is trivial. When we use native ID generation we normally would not want the property to be settable by user code. As long as we only deal with ITestEnties in our app, this is not problematic. However, NHibernate is still complaining:
Could not find a setter for property 'ID' in class NHTest.ITestEntity'
Adding the setter to the interface is easy, but will also allow any client to set the ID manually - something we do not want.
The solution
I came up with a solution to the problem which is simple, easy to reuse and works without to much "hacking".
First I wrote an interface that provides read/write access to the ID:
1: /// <summary>
2: /// Interface for objects that have an id.
3: /// </summary>
4: /// <typeparam name="TId">The type of the id.</typeparam>
5: public interface IIdentified<TId> {
6: /// <summary>
7: /// Gets or sets the ID.
8: /// </summary>
9: /// <value>The ID.</value>
10: TId ID { get; set; }
11: }
I then introduced an interface that derives from IIdentified and which hides the setter of its parent:
1: /// <summary>
2: /// Interface for domain entities.
3: /// </summary>
4: /// <typeparam name="TId">The type of the id.</typeparam>
5: public interface IEntity<TId> : IIdentified<TId>{
6: /// <summary>
7: /// Gets the ID.
8: /// </summary>
9: /// <value>The ID.</value>
10: /// <remarks>This property hides <see cref="IIdentified{TId}.ID"/></remarks>
11: new TId ID { get; }
12: }
Now I introduced a convenient abstract base class that implements IEntity<ID> (and indirectly IIdentified<ID>)
1: public abstract class Entity<TId> :IEntity<TId> {
2: private TId id;
3: public TId ID { get { return id; }}
4:
5: TId IIdentified<TId>.ID {
6: get { return id; }
7: set { id = value; }
8: }
9:
10: }
By implementing IIdentified<TId>.ID explicitly I hide the setter from users of IEntity<TId> and Entity<TId>. I can now derive my proxy interface and the entity class from IEntity and Entity:
1: /// <summary>
2: /// Proxy interface
3: /// </summary>
4: public interface ITestEntity : IEntity<int> {
5: }
6:
7: /// <summary>
8: /// Entity class
9: /// </summary>
10: public class TestEntity : Entity<int>, ITestEntity {
11: }
NHibernate is smart enough to detect that IIdentified provides a setter to ID. On the other hand, users of IEntity and Entity will only see the read-only version of ID.
Using ITestEntity:
Using TestEntity:
Of course the setter is available as soon as you cast the entity to an IIdentified<int>:
On the one hand this allows us to inject the ID for example in unit tests. On the other hand it is not very likely that the user will deal with IIdentified<TId> references. So unless the user explicitly converts the entity to an IIdentified<TId> he/she won't see the setter. For my scenarios this is certainly enough.