Is it possible to make Unity inject dependent object into parent on demand instead of when parent object is being initialized?
2 Answers
Yes, have Unity inject a Lazy<T> instead of a T. Here's one article on that subject: http://www.tomdupont.net/2012/07/lazy-unity-injection.html
Comments
Lazy<T> doesn't really help if you truly need "On Demand." Lazy<T> really only works if you need deferred resolving. But then after the initial resolve, its ok to have the same reference.
I solved this by using Castle.DynamicProxy. I needed to have certain dependencies be injected "On Demand" meaning they needed to be resolved at time of use (new resolve every time), not at time of "Depender" build up.
To do this I configure my container like so:
private void UnityRegister(IUnityContainer container)
{
container.RegisterType<HttpContextBase>(new OnDemandInjectionFactory<HttpContextBase>(c => new HttpContextWrapper(HttpContext.Current)));
container.RegisterType<HttpRequestBase>(new OnDemandInjectionFactory<HttpRequestBase>(c => new HttpRequestWrapper(HttpContext.Current.Request)));
container.RegisterType<HttpSessionStateBase>(new OnDemandInjectionFactory<HttpSessionStateBase>(c => new HttpSessionStateWrapper(HttpContext.Current.Session)));
container.RegisterType<HttpServerUtilityBase>(new OnDemandInjectionFactory<HttpServerUtilityBase>(c => new HttpServerUtilityWrapper(HttpContext.Current.Server)));
}
The idea being that I provide a method to retrieve the instance "on demand." The lambda gets invoked whenever any of the methods of the instance are used. The Dependent object is actually holding a reference to a proxied object, not the object itself.
OnDemandInjectionFactory:
internal class OnDemandInjectionFactory<T> : InjectionFactory
{
public OnDemandInjectionFactory(Func<IUnityContainer, T> proxiedObjectFactory) : base((container, type, name) => FactoryFunction(container, type, name, proxiedObjectFactory))
{
}
private static object FactoryFunction(IUnityContainer container, Type type, string name, Func<IUnityContainer, T> proxiedObjectFactory)
{
var interceptor = new OnDemandInterceptor<T>(container, proxiedObjectFactory);
var proxyGenerator = new ProxyGenerator();
var proxy = proxyGenerator.CreateClassProxy(type, interceptor);
return proxy;
}
}
OnDemandInterceptor:
internal class OnDemandInterceptor<T> : IInterceptor
{
private readonly Func<IUnityContainer, T> _proxiedInstanceFactory;
private readonly IUnityContainer _container;
public OnDemandInterceptor(IUnityContainer container, Func<IUnityContainer, T> proxiedInstanceFactory)
{
_proxiedInstanceFactory = proxiedInstanceFactory;
_container = container;
}
public void Intercept(IInvocation invocation)
{
var proxiedInstance = _proxiedInstanceFactory.Invoke(_container);
var types = invocation.Arguments.Select(arg => arg.GetType()).ToArray();
var method = typeof(T).GetMethod(invocation.Method.Name, types);
invocation.ReturnValue = method.Invoke(proxiedInstance, invocation.Arguments);
}
}