NHibernate + Generics = Explained a bit better
A friend of mine pointed out my post on "NHibernate + Generics = Small Codebase" was a bit vague so I've added this which might clarify.
What I was trying to say was that combining generics with NHibernate makes writing and testing persistence code very easy. For example here's some code that could save a persistent class using NHibernate.
Session.Save(toSave);
You want that code to be somewhere outside your domain objects, Evans says to put them in classes called Repositories. You'd have at least one repository per aggregate root, so you are going to end up with quite a few of them.
A base class thus makes sense as all the repositories have a lot of behavior in common. However you want type safety on the methods so putting this in the base class just doesn't cut it:
public virtual void Save(object toSave)
The solution is obvious, you just use generics with a repository base class:
public sealed class MyRootRepository : NHibernateRepository<TMyRoot, int>
{
.... custom code including extra queries ...
}
The base class can thus use the type of object its persisting (MyRoot here) and the type of ID field (int here) when it needs it.
I think there is also a big advantage when using generics for persistence testing. For example we have specific tests we want to do for all aggregate roots including concurrency/update/save. We want to do the tests to check the database AND the mapping files are correct but we write the same code each time so we use inheritance from a generic base class:
[TestClass]
public sealed class MyRootPersistenceTests : AggregateRootPersistenceTestBase<Myroot, int>
{
[TestMethod]
public void CanPersist()
{
TestPersistence();
}
[TestMethod]
public void CanUpdate()
{
TestUpdate();
}
[TestMethod]
[ExpectedException(typeof(StaleObjectStateException))]
public void CanHandleConcurrency()
{
TestConcurrency();
}
# region Overridden Members
protected override MyRoot CreateAggregateRoot()
{
// create and return a MyRoot
}
protected override void ModifyAggregateRoot(MyRoot original)
{
// modify original
}
# endregion
}
We have similar base classes for a few other situations (non-root aggregate parts, read-only reference data). In each case the vast majority of the code is in the base classes (or other classes they call) and we just use the template method pattern by calling into the abstract members (such as CreateAggregateRoot).
Note that the base class can create an instance of the repository, because we decided to make our NHibernateRepository
- Get the object by calling CreateAggregateRoot
- Save the object to the database
- Reload the object from the database
- Use the ObjectComparer to compare the original and reloaded objects.
Unfortunately each test class must have the test methods (CanHandleConcurrency etc) because although NUnit let you have test methods on a base class VSTS doesn't. Drat.
Anyway this has proved to be very useful and has definitely been a big time saver as we've been moving code across to using NHibernate. I've found my current working style is:
- Use TDD to develop the new feature.
- If the new feature involved the addition of new fields that need to be persisted then run the persistence tests.
- The persistence tests should now fail because when we reload the object and pass it to ObjectComparer it will find that the original object had a value in the new field/property whilst the reloaded object will have the default value.
- Add the appropriate content to the mapping file so that the new field is persisted.
- Run the tests and they should now pass.
Oh and ignore the repository base class name and the method names, we have reasons for keeping them that way the minute.
I think I see what you mean, although there isnt any actual generics code in this post! :-)
ReplyDelete(feel free to delete comment rather than leave it up. Think of this as a lazy way of TXTing you)
There are now, I forgot you can't directly use "<>" in HTML.
ReplyDeletebegin shameless plug
ReplyDeleteOr you could use GenWise Studio with the NHibernate templates that does generate all the mapping files, unit and factories for you based on your database scheme
http://www.genwise.com
end shameless plug
There are a million template-based code generators out that which can produce stacks of files with the content you need. Your comment is in no way relevant to the topic of discussion.
ReplyDeleteRhino.Commons includes a very nice and complete generic base class Repostory specifically designed for use with NHibernate.
ReplyDeleteOr... you could just step up and use Castle's ActiveRecord.
sillyevar, thanks for the heads up on Rhino.Common link and it looks like it could be useful.
ReplyDeleteI must admit I'm not a fan of Active Record, I personally prefer the seperation between the domain model and the persistence related responsibilites which is why I quite like using repositories.