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