NHibernate with Inversion of Control
This is just something I've been thinking about and was wondering if it exists, or even if its beneficial at all.
I'm doing inversion of control and dependency injection using Unity. I'm also doing ORM with fluent nHibernate. I was wondering if there was a way to either configure nHibernate to take interfaces as its type parameters and do IoC for me, or what the best way to use them together would be.
For instance, if I had a customer object using a repository pattern I would possibly have 2 interfaces (ICustomer, ICustomerRepository) as well as 2 concrete implementations (Customer, CustomerRepository). In the concrete CustomerRepository I would have to tie it directly to the Customer object in order to use nHIbernate.
pub开发者_StackOverflow中文版lic class CustomerRepository : ICustomerRepository
{
public ICustomer Retrieve(int id)
{
return _session.Get<Customer>(id);
}
}
Instead of passing "Customer" as a type parameter to the session, I think it would be cool to pass "ICustomer" and somehow have nHibernate configured to do the IoC. Is this even possible, or beneficial at all?
When we started designing our application, I had the exact same idea. However, pretty soon we ran into some issues with using only interface based types, so we compromised, by using concrete types and loose repositories, meaning Customer and ICustomerRepository.
I'm trying to recall what issues we ran into, but right now I'm blanked out.
There is no real advantage in throwing in an ICustomer
into NHibernate. NHibernate itself should just be a black box with a couple of hooks where you can attach your mocks to. As you can mock the implementation in NHibernate; it doesn't really care what objects are used inside.
When mocking this method, you can do all of this with the ISession
and IQuery
from NHibernate, and the ICustomerRepository
from your own code. No need to add an extra abstraction.
Oh, and btw, why have NHibernate as an additional IoC container when your repository already is?
One way to integrate your IOC container (Unity) with NHibernate, is to use Unity to resolve the type you'll pass to NHibernate.
This accomplishes what I think is your aim, which is to have in only one place the mapping between interface and implementation.
public CustomerRepository : ICustomerRepository
{
Type customerType;
// ISession[Factory] injection omitted for brevity
public CustomerRepository(IUnityContainer container)
{
registration = container.Registrations.FirstOrDefault(
x => x.RegisteredType.Equals(ICustomer));
if(registration == null)
{
throw new ApplicationException(
"No ICustomer implementation was registered.");
}
customerType = registration.MappedToType;
}
public ICustomer Retrieve(int id)
{
return _session.Get(customerType, id);
}
}
Obviously, you can't use NHibernate's generic overloads, but I think they all have non-generic equivalents.
The one other place in which you'll have to reference the concrete implementation is in your FNH ClassMap<T>
s.
I can't see any great flexibility from doing this but to achieve what you're asking you could try:
public abstract class AbstractCustomerRepository<T> : ICustomerRepository where T : class, ICustomer
{
public ICustomer Retrieve(int id)
{
return _session.Get<T>(id);
}
}
public class CustomerRepository : AbstractCustomerRepository<Customer>
{
}
An example of using IOC would be to find the concrete implementation of ICustomerRepository
// in your client code.
ICustomerRepository dao = ServiceFactory.GetServiceInstance<ICustomerRepository>();
// a framework of the ServiceFactory
public static class ServiceFactory
{
private WindsorContainer m_container;
public static T GetServiceInstance<T>()
{
// use your IOC to resolve your <T>
return m_container.Resolve<T>();
}
}
In the example above, I am using Castle Windsor as my IOC. Please adapt your implementation to use Unity Block.
I hope you'd get my idea across.
If you're going to have an ICustomer interface, this would suggest you want to substitute Customer for something else? Would this be the case? If so then with what?
Your Customer class should be part of your domain, along with other entities such as Product, Order etc etc. Your domain should form the central part of your entire application. I think your efforts should move toward keeping your domain entities decoupled from your NHibernate data access code, which you seem to be achieving with Repository interfaces.
If you want to create a base Customer with specific Customer implementations, then use inheritance, which Nhibernate supports really well:
public abstract class Customer { }
public class EnterpriseCustomer : Customer { }
public class SmbCustomer : Customer { }
public class IndividualCustomer : Customer { }
NHibernate is powerful enough to instantiate the correct type when you call return _session.Get<Customer>(id);
without having to explicitly cast it yourself.
Perhaps this is what you're after. Have a look at the NHibernate documentation on inheritance: http://nhibernate.info/doc/nh/en/index.html#inheritance
精彩评论