The Goal:
I am trying to dynamically register delegates for creating objects in simple injector container. Basically I want to be able not only to get actual instances from DI container but also a methods that could be used to create those instances. This could be useful for lazy object creation. For example if I have a service with a lot of dependencies, I don't want them to be created on service object creation but only when those specific dependencies need to be used.
The Problem:
When I usecontainer.Register<T> method I can successfully register delegates:
container.Register<Func<IRepository>>(() => container.GetInstance<IRepository>);
The problem is when I want to register those kind of delegates dynamically when the type is known only at runtime:
Here is my code:
private static void RegisterFunctions(Container container)
{
var types = container.GetCurrentRegistrations()
.Where(r => r.ServiceType.IsInterface && r.ServiceType != typeof(Func<>))
.Select(r => r.ServiceType);
var typesList = types as IList<Type> ?? types.ToList();
foreach (var t in typesList)
{
var typeToRegister = typeof(Func<>).MakeGenericType(t);
//This needs to be replaced:
container.Register(typeToRegister, () => container.GetInstance(t));
}
}
The problem is in container.Register(typeToRegister, () => container.GetInstance(t)); I assumed that it's behavior will be the same as container.Register<T> method but I was wrong.
When I run this code for the following scenario:
public interface IProductService
{
}
public class ProductService : IProductService
{
public ProductService(Func<IProductRepository> getRepositoryFunc)
{
this.ProductRepositoryFunc = getRepositoryFunc;
}
}
I am getting an System.InvalidCastException:
[InvalidCastException: Unable to cast object of type 'XXX.ProductRepository' to type 'System.Func'1[XXX.IProductRepository]'.]
lambda_method(Closure ) +83
lambda_method(Closure ) +179
SimpleInjector.Scope.CreateAndCacheInstance(ScopedRegistration'2 registration) +74
SimpleInjector.Scope.GetInstance(ScopedRegistration'2 registration) +260 SimpleInjector.Scope.GetInstance(ScopedRegistration'2 registration, Scope scope) +207
SimpleInjector.Advanced.Internal.LazyScopedRegistration'2.GetInstance(Scope scope) +241 lambda_method(Closure ) +310
SimpleInjector.InstanceProducer.GetInstance() +117[ActivationException: The registered delegate for type ProductController threw an exception. Unable to cast object of type 'XXX.ProductRepository' to type 'System.Func'1[XXX.IProductRepository]'.]
SimpleInjector.InstanceProducer.GetInstance() +222
SimpleInjector.Container.GetInstance(Type serviceType) +148
SimpleInjector.Integration.Web.Mvc.SimpleInjectorDependencyResolver.GetService(Type serviceType) +137
System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +87[InvalidOperationException: An error occurred when trying to create a controller of type 'YYY.ProductController'. Make sure that the controller has a parameterless public constructor.] System.Web.Mvc.DefaultControllerActivator.Create(RequestContext requestContext, Type controllerType) +247
System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType) +438
System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName) +257
System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +326 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +157
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +88
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +50
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
I understand the exception and the issue that causes it but I am struggling to find a correct way to implement the described functionality.
So if I summarize my question:
Is there a way to perform a registration similar to:
container.Register<Func<IRepository>>(() => container.GetInstance<IRepository>)
for a dynamic type that that resolved in runtime, something like
container.Register<Func<T>>(() => container.GetInstance<T>)