Encapsulating NHibernate-specific calls within a custom repository

211 views
Skip to first unread message

Billy McCafferty

unread,
Mar 12, 2012, 2:26:54 PM3/12/12
to S#arp Lite
In a previous post, Patrick brought up the following question
(paraphrased here for clarity): How do you support NHibernate-
specific calls without making the higher levels dependent on
NHibernate?

It's easiest to explain with a scenario... Suppose you have an
accounting system with an object called LedgerEntry. Further, suppose
that LedgerEntry has associations to a Project object and a Client
object (which has an association to Address).

Now suppose that you'd like to make a report which requires some funky
joining among LedgerEntry, Client, Address, and Project. If the LINQ
to NHibernate provider isn't up to the task, you can create a custom
repository to encapsulate the NHibernate-specific details. Here are
the general steps for doing so, in a nutshell:

1) To MyProject.Domain/RepositoryInterfaces, add
ILedgerEntryRepository.cs which looks as follows:

public interface ILedgerEntryRepository : IRepository<LedgerEntry> {
SomeReportDto GetSomeReport();
}

2) To MyProject.NHibernateProvider/Repositories, add
LedgerEntryRepository.cs which looks as follows:

public class LedgerEntryRepository : Repository<LedgerEntry>,
ILedgerEntryRepository {
public SomeReportDto GetSomeReport() {
// Using NHibernate's Session object directly,
// leverage ICriteria, HQL or whatever to query for your report
data
}
}

3) Inject the ILedgerEntryRepository into your controller or tasks
object; e.g.:

public class LedgerEntryReportingTasks {
public LedgerEntryReportingTasks(ILedgerEntryRepository
ledgerEntryRepository) {
_ledgerEntryRepository = ledgerEntryRepository;
}

// Communicate with the repository's interface, not the concrete
repository itself
public SomeReportDto GetSomeReport() {
return _ledgerEntryRepository.GetSomeReport();
}

private readonly ILedgerEntryRepository _ledgerEntryRepository;
}

That's all there is to it. The IoC container (e.g., StructureMap will
take care of the dependency injection). In the above example, it
would likely be overkill to have the tasks class abstract the call to
_ledgerEntryRepository.GetSomeReport(). Instead, your controller
could call it directly (and have injected into the controller in the
same way). But I wanted to demonstrate that it can be equally
injected into the tasks layer if you'd prefer. You've now
encapsulated low-level NHibernate functionality without introducing
unnecessary DLL dependencies, and enabling your controllers/tasks to
be easily unit tested with mocks/stubs in place of the repository.

If anyone would like to read more on the subject of introducing and
using separated interfaces and dependency injection in general, I'd
also recommend:

* Refactoring Service Dependencies to Separated Interface -
http://devlicio.us/blogs/billy_mccafferty/archive/2008/10/30/refactoring-service-dependencies-to-separated-interface.aspx
* Dependency Injection 101 (for anyone who needs a from-the-basics
introduction) - http://devlicio.us/blogs/billy_mccafferty/archive/2009/11/09/dependency-injection-101.aspx

Hope this helps!

Billy McCafferty

Billy McCafferty

unread,
Mar 12, 2012, 2:55:02 PM3/12/12
to S#arp Lite
To be clear, your custom repository doesn't *have* to implement
IRepository<>; e.g., you could have POCOs
MyProject.Domain.ILedgerEntryReportGenerator and
MyProject.NHibernateProvider.LedgerEntryReportGenerator.

But implementing IRepository<>/Repository<> makes it a tad bit easier
to get quick access to the NHibernate Session object and to decorate
the existing repository (in this case IRepository<LedgerEntry> with
new capabilties. If you wanted a more POCO approach, the concrete
LedgerEntryReportGenerator would need to emulate accepting the
ISessionFactory as demonstrated by
https://github.com/codai/Sharp-Lite/blob/master/SharpLiteSrc/app/SharpLite.NHibernateProvider/Repository.cs

This reminds me that there's a bug in the steps I provided previously;
step 2 needs to reflect:

-----------

2) To MyProject.NHibernateProvider/Repositories, add
LedgerEntryRepository.cs which looks as follows:

public class LedgerEntryRepository :
Repository<LedgerEntry>, ILedgerEntryRepository {

// The IoC will take care of injecting ISessionFactory
public LedgerEntryRepository(ISessionFactory sessionFactory)
: base(sessionFactory) { }

public SomeReportDto GetSomeReport() {
// Using NHibernate's Session object directly,
// leverage ICriteria, HQL
// or whatever to query for your report data
}
}

-----------

Billy McCafferty


On Mar 12, 12:26 pm, Billy McCafferty <googlegro...@emccafferty.com>
wrote:
> * Refactoring Service Dependencies to Separated Interface -http://devlicio.us/blogs/billy_mccafferty/archive/2008/10/30/refactor...
> * Dependency Injection 101 (for anyone who needs a from-the-basics
> introduction) -http://devlicio.us/blogs/billy_mccafferty/archive/2009/11/09/dependen...

Patrick

unread,
Mar 14, 2012, 9:55:47 PM3/14/12
to sharp...@googlegroups.com
Thanks for taking up the challenge.  I see how it fits together and works; it seems like a reasonable solution for the most part.  Very similar to the IQueryForXxx from the sample project.  I currently have some stored procedures being called this way, with the interface in the Domain layer and the actual SP call still in the NHibernateProvider.  The only problem I have is that sometimes it seems like I'm just creating layers of wrappers, but I guess that is just the price of abstraction!

I struggle when nHibernate has something that I'd like to use directly or is soooo close.  The most recent example would be fetching; there are pages in which I know I'll need multiple bags from the Model so I would like to have them eager loaded for that particular page.  nHibernate's Fetch() seems like the perfect solution but I can't access it at the web layer.  I can definitely wrap it with my own layers as SlyNet posted in the other thread, it just doesn't have a great feel to it.  Don't get me wrong, it isn't so much a criticism of the architecture because in general I really do like the strong separation, it just adds a couple of hoops sometimes.  It should pay off in terms of maintainability.

However, I would be interested in what you would typically do for eager loading in only certain situations.  The three things I considered so far are:
  1. Wrapped up fetch calls so the controller can eager load what it needs for the page.  This seems to fit well with the controllers calling Get() and GetAll().
  2. Wrapped up fetch calls used by the Tasks layer to return a more populated domain entity.  This would make more of the simple calls (Index method) go through the tasks layer and I'm not sure it would provide much benefit.
  3. Use IQueryXxx or IXxxRepository that are implemented in the nHibernate layer and can return fully populated entities.
I realize some of this is probably layered architecture basics, but honestly my experience so far has been maintaining pretty poorly structured systems so I'm still learning.  I don't want another dung heap added to our maintenance pile.

-Patrick
Reply all
Reply to author
Forward
0 new messages