First I will Try to define the Problem : The Constructor Injection pattern is easy to understand until a follow-up question comes up:
Where should we compose object graphs?
It’s easy to understand that each class should require its dependencies through its constructor, but this pushes the responsibility of composing the classes with their dependencies to a third party. Where should that be?
It seems to me that most people are eager to compose as early as possible, but the correct answer is:
As close as possible to the application’s entry point.
This place is called the Composition Root of the application and defined like this:
A Composition Root is a (preferably) unique location in an application where modules are composed together.
This means that all the application code relies solely on Constructor Injection (or other injection patterns), but is never composed. Only at the entry point of the application is the entire object graph finally composed.
The appropriate entry point depends on the framework:
- In console applications it’s the Main method
- In ASP.NET MVC applications it’s global.asax and a custom IControllerFactory
- In WPF applications it’s the Application.OnStartup method
- In WCF it’s a custom ServiceHostFactory
- etc
The Composition Root is an application infrastructure component.Only applications should have Composition Roots. Libraries and frameworks shouldn’t.
The Composition Root can be implemented with Poor Man’s DI, but is also the (only) appropriate place to use a DI Container.
A DI Container should only be referenced from the Composition Root. All other modules should have no reference to the container.
Using a DI Container is often a good choice. In that case it should be applied using the Register Resolve Release pattern entirely from within the Composition Root.
Read more in Dependency Injection in .NET.
Having seen the benefits of a Composition Root over the Service Locator anti-pattern global That entailed an Unity container,
The solution Consists of two classes. DependencyFactory is the public wrapper for an internal class, DependencyFactoryInternal. The outer class presents easy-to-use static methods and acquires the right kind of lock (read or write) for whatever operations you’re Trying to do .
First time you access the container, the code Will the load-type registrations in your. Config file. You can register aussi kinds programmatically with the static RegisterInstance method.
DependencyFactory.RegisterInstance <IMyType> (
new
MyConcreteType ());
To resolve a type use the static Resolve method
var myObject = DependencyFactory.Resolve <IMyTypeType> ();
Finally, the static FindRegistrationSingle method exposed an existing ContainerRegistration .
public sealed class DependencyFactory : IDisposable { #region Properties /// <summary> /// Get the singleton Unity container. /// </summary> public IUnityContainer Container { get { return DependencyFactoryInternal.Instance.Container; } } #endregion #region Constructors /// <summary> /// Default constructor. Obtains a write lock on the container since this is the safest policy. /// Also uses a relatively long timeout. /// </summary> public DependencyFactory() : this(true, 10000) { } /// <summary> /// Construct an object that can access the singleton Unity container until the object is Disposed. /// </summary> /// <param name="allowContainerModification">True to allow modification of the container. /// The caller is responsible for behaving according to this pledge! /// The default is to allow modifications, which results in a write lock rather than /// the looser read lock. If you're sure you're only going to be reading the container, /// you may want to consider passing a value of false.</param> /// <param name="millisecondsTimeout">Numer of milliseconds to wait for access to the container, /// or -1 to wait forever.</param> internal DependencyFactory(bool allowContainerModification, int millisecondsTimeout = 500) { if (allowContainerModification) DependencyFactoryInternal.Instance.EnterWriteLock(millisecondsTimeout); else DependencyFactoryInternal.Instance.EnterReadLock(millisecondsTimeout); } #endregion #region Methods /// <summary> /// Resolves a type in the static Unity container. /// This is a convenience method, but has the added benefit of only enforcing a Read lock. /// </summary> /// <param name="overrides">Overrides to pass to Unity's Resolve method.</param> /// <typeparam name="T">The type to resolve.</typeparam> /// <returns>An concrete instance of the type T.</returns> /// <remarks> /// If you already have a DependencyFactory object, call Resolve on its Container property instead. /// Otherwise, you'll get an error because you have the same lock on two threads. /// </remarks> static public T Resolve<T>(params ResolverOverride[] overrides) { using (var u = new DependencyFactory(false, 10000)) { return u.Container.Resolve<T>(overrides); } } /// <summary> /// Convenience method to call RegisterInstance on the container. /// Constructs a DependencyFactory that has a write lock. /// </summary> /// <typeparam name="T">The type of register.</typeparam> /// <param name="instance">An object of type T.</param> static public void RegisterInstance<T>(T instance) { using (var u = new DependencyFactory()) { u.Container.RegisterInstance<T>(instance); } } /// <summary> /// Find the single registration in the container that matches the predicate. /// </summary> /// <param name="predicate">A predicate on a ContainerRegistration object.</param> /// <returns>The single matching registration. Throws an exception if there is no match, /// <remarks> /// Only uses a read lock on the container. /// </remarks> /// or if there is more than one.</returns> static public ContainerRegistration FindRegistrationSingle(Func<ContainerRegistration, bool> predicate) { using (var u = new DependencyFactory(false,10000)) { return u.Container.Registrations.Single(predicate); } } /// <summary> /// Acquires a write lock on the Unity container, and then clears it. /// </summary> static public void Clear() { using (var u = new DependencyFactory()) { DependencyFactoryInternal.Instance.Clear(); } } #endregion #region IDisposable Members /// <summary> /// Dispose the object, releasing the lock on the static Unity container. /// </summary> public void Dispose() { if (DependencyFactoryInternal.Instance.IsWriteLockHeld) DependencyFactoryInternal.Instance.ExitWriteLock(); if (DependencyFactoryInternal.Instance.IsReadLockHeld) DependencyFactoryInternal.Instance.ExitReadLock(); } #endregion }
Internal class
/// This class is internal; consumers should go through DependencyFactory. /// </remarks> internal class DependencyFactoryInternal { #region Fields // Lazy-initialized, static instance member. private static readonly Lazy<DependencyFactoryInternal> _instance = new Lazy<DependencyFactoryInternal>(() => new DependencyFactoryInternal(), true /*thread-safe*/ ); private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); #endregion #region Properties /// <summary> /// Get the single instance of the class. /// </summary> public static DependencyFactoryInternal Instance { get { return _instance.Value; } } IUnityContainer _container = null; /// <summary> /// Get the instance of the Unity container. /// </summary> public IUnityContainer Container { get { try { return _container ?? (_container = new UnityContainer().LoadConfiguration()); } catch (Exception ex) { throw new InvalidOperationException("Could not load the Unity configuration.", ex); } } } /// <summary> /// Tells whether the underlying container is null. Intended only for unit testing. /// (Note that this property is internal, and thus available only to this assembly and the /// unit-test assembly.) /// </summary> internal bool ContainerIsNull { get { return _container == null; } } /// <summary> /// Tell whether the calling thread has a read lock on the singleton. /// </summary> internal bool IsReadLockHeld { get { return _lock.IsReadLockHeld; } } /// <summary> /// Tell whether the calling thread has a write lock on the singleton. /// </summary> internal bool IsWriteLockHeld { get { return _lock.IsWriteLockHeld; } } #endregion #region Constructor /// <summary> /// Private constructor. /// Makes it impossible to use this class except through the static Instance property. /// </summary> private DependencyFactoryInternal() { } #endregion #region Methods /// <summary> /// Enter a read lock on the singleton. /// </summary> /// <param name="millisecondsTimeout">How long to wait for the lock, or -1 to wait forever.</param> public void EnterReadLock(int millisecondsTimeout) { if (!_lock.TryEnterReadLock(millisecondsTimeout)) throw new TimeoutException(Properties.Resources.TimeoutEnteringReadLock); } /// <summary> /// Enter a write lock on the singleton. /// </summary> /// <param name="millisecondsTimeout">How long to wait for the lock, or -1 to wait forever.</param> public void EnterWriteLock(int millisecondsTimeout) { if (!_lock.TryEnterWriteLock(millisecondsTimeout)) throw new TimeoutException(Properties.Resources.TimeoutEnteringWriteLock); } /// <summary> /// Exit the lock obtained with EnterReadLock. /// </summary> public void ExitReadLock() { _lock.ExitReadLock(); } /// <summary> /// Exit the lock obtained with EnterWriteLock. /// </summary> public void ExitWriteLock() { _lock.ExitWriteLock(); } /// <summary> /// Clear the Unity container and the lock so we can restart building the dependency injections. /// </summary> /// <remarks> /// Intended for unit testing. /// </remarks> public void Clear() { if (_container != null) _container.Dispose(); _container = null; while (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); while (_lock.IsReadLockHeld) _lock.ExitReadLock(); } #endregion }