dinsdag 18 november 2008

Using a Factory for resolving a dependency

Today I solved the gap between Dependency Injection with Unity and a Factory. What was the question?

We have a UseCaseFactory for creating instances of descendants of the UseCaseBase class. This factory internally uses the Microsoft Patterns & Practices Unity container for actually instantiating the requested class and its dependencies. When a UseCase is created, it gets it's own container.

To make things even more complex, we have UseCaseFactory.CreateChildUseCase, which results in a UseCase in the same container as the parent UseCase.

Some descendants - but not all - require repositories for data handling and therefore a Hibernate session. Currently there is a HibernateSessionFactory as well. The NewSession() method can provide you with a session.

The old solution was ugly: All UseCase classes that required a repository had to be derived from UseCaseWithRepositoriesBase. The CreateUseCase method would check on this. If true, then it created a Session with the HibernateSessionFactory and registered this instance in the container. After that, all necessary repositories (that depend on a session) could be resolved.

I did not want to completely change the creation of Hibernate session objects just to get rid of the factory. It took me a little while to find out what I really wanted: Register a method on a factory in the container to use when resolving the Hibernate session.

There are not many hits on it in Google, but it is possible. The Unity framework provides the StaticFactoryExtension exactly for this purpose. Here's how it works:

Imports Microsoft.Practices.Unity
Imports Microsoft.Practices.Unity.StaticFactory

Public Class UsecaseFactory

Private Shared mHibernateFactoryExtension As New StaticFactoryExtension()

Shared Sub New()
mRootContainer = New UnityContainer

mRootContainer.AddExtension(mHibernateFactoryExtension)
mHibernateFactoryExtension.RegisterFactory(Of HibernateSession)(AddressOf HibernateSessionFactory.CreateSession)
End Sub
End Class

Effectively you tell the container: If a HibernateSession instance is requested and it does not yet exist, call HibernateSessionFactory.CreateSession to create it for you.

You have to provide the RegisterFactory method with a delegate of type FactoryDelegate:

Public Delegate Function FactoryDelegate(ByVal container As Microsoft.Practices.Unity.IUnityContainer) As Object

Although you get the container as a parameter, you need not register the result yourself. You just have to return the requested object (in this case a HibernateSession object). It will automatically be registered in the container. The container is probably just there for your convenience in case you need to resolve other classes along the way.

Geen opmerkingen: