Enterprise Library 5.0: Some Architecture Changes
The Enterprise Library team has been hard at work on some of our initial stories. One of the bigger initial stories is what we’ve affectionately call ARC01. I know, very descriptive, so let me explain a bit of what this is about.
The Doublemint Twins
Enterprise Library 4.0 (and 4.1) actually had two ways of locating and building object for its various blocks: through static factories or through a Unity dependency injection container.
Actually, having two approaches to building up objects in Enterprise Library 4.0 was intentional. Honest. Really. I mean it. While it may seem a bit odd, this was an intermediate step to moving entirely to a container approach for Enterprise Library.
This move is important for a number of reasons (and not just because we also wrote the Unity container). First, this is important because the static factory approach does not work well for those people who use containers in their applications and want to be able to get Enterprise Library objects from their container. Second, it is important because the instance factories in Enterprise Library 4.0 are handling things that containers are designed to handle, such as lifetime, location, and object creation. It doesn’t make sense to build and maintain these specifically for Enterprise Library.
Enterprise Library 4.0 took a step towards the container world by providing an approach to
configuring a Unity container with the appropriate Enterprise Library pieces. However, this means that you generally want to use either the static factory approach or the container approach, because objects created in one approach do not work with objects in another approach. We were really maintaining two sets of objects and building strategies (that is, the approach to building up those objects in case we need to rebuild them).
As the final step towards moving to using a single container, we now have the static/instance factories that rely on the same container that you could configure independently.
So you can use the static factories, instance factories, or the container and they will all work together.
While this sounds easy, plugging container B into slot A is not as straightforward as you might think. To start with, Enterprise Library 4.0 relied on many ObjectBuilder internals to build objects and frequently would apply configuration during object creation:
We wanted to use the containers in a more traditional way, namely by providing the configuration ahead of time and then using the container to resolve objects, so now we configure the container the first time something needs to be resolved:
After the container has been configured, the EnterpriseLibraryContainer will directly reference the UnityContainer.
Why Can’t We All Just Get Along?
Aside from making the Enterprise Library story a bit cleaner, moving to a container-based approach has another benefit; it makes it easier to provide support for other containers to work with Enterprise Library. By default, the EnterpriseLibraryContainer provides EnterpriseLibrary objects through a UnityContainer but it does so through the IServiceLocatorinterface provided by the Common Service Locator project. The Common Service Locator was a join effort of thought leaders and key contributors representing various containers (including Unity, Spring.NET, Castle/Windsor, MEF, StructureMap, Ninject).
The IServiceLocator serves as a plug point for Enterprise Library to work with a container other than Unity. The IServiceLocator only deals with how to acquire object instance, we also needed a way to let a container know how it should be configured to support Enterprise Library without letting Enterprise Library know anything about a specific container. We provide this configuration information in a container agnostic way through a set of TypeRegistrations. Generally, atype registration will contain information about how a type should be built (what constructors and parameter values to use, whether to create them as a singleton or instance each time, and what properties to set on the type). Here’s a typical type registration:
1: new TypeRegistration<Database>(
2: () => new SqlDatabase(
3: ConnectionString,
4: Container.Resolved<IDataInstrumentationProvider>(Name)))
5: {
6: Name = Name,
7: Lifetime = TypeRegistrationLifetime.Transient
8: };
9:
Type registrations are specified as expression trees as it makes them a bit easier to read and they tend to work better when refactoring code. The above registration can basically be interpreted as ‘register a type Database named Name that is mapped to a new instance each time of type SqlDatabase and use the constructor that takes a ConnectionString and an IDataInstrumentationProvider (oh, and by the way, can you pretty please resolve that from the container too?)’.
These registrations are processed by a configurator that knows how to apply them to a specific container. Enterprise Library provides a UnityContainerConfigurator, but you can use any other IContainerConfigurator. The general configuration process looks like this:
Configuring Enterprise Library, given a configuration source and a container configurator can be done like this:
1: var configurator = new UnityContainerConfigurator(Container);
2:
3: EnterpriseLibraryContainer.ConfigureContainer(
4: configurator,
5: configurationSource);
Hopefully this gives you a little deeper understanding of the motivation and content for recent changes in Enterprise Library. In future posts, I hope to go into a bit more depth around how we find type registrations and some of the cleanup we did around instrumentation.
del.icio.us Tags: entlib Enterprise Library p&p
Comments
- Anonymous
June 23, 2009
The comment has been removed - Anonymous
July 01, 2009
Nice Write up.Thank you for the information. - Anonymous
May 11, 2011
How can we get the values from the Expression inside the TypeRegistration ConstructorParameters property? Here is my work so far: www.nikosbaxevanis.com/.../error-management-is-sometimes-exceptionally-difficult.html The Expression property on ParameterValue is either a MemberExpression or a MethodCallExpression. On the MethodCallExpression how can we get the values?