I have a ICommand interface and tasks that are using dependencies injected by constructor. Dependencies are using different constructors so they have to be resolved by the request itself. I want to tell my container how to resolve some dependencies in the specific context it's being resolved.
interface ICommand
{
string Do();
}
interface IUser
{
string Name { get; }
}
class Welcome : ICommand
{
IUser _user;
public Welcome(IUser user)
{
_user = user;
}
public string Do()
{
return "Hello, "+_user.Name;
}
}
class OAuthUser : IUser
{
// use remote service to get data
public OAuthUser (IOAuthService service, JsonWebToken token)
{
// to be implemented
}
}
class TemporaryTokenUser : IUser
{
// use sql to check if user has temporary token
public TemporaryTokenUser (IDbConnection db, string token)
{
// to be implemented
}
}
class UserPasswordUser : IUser
{
// try authenticating user with credentials
public UserPasswordUser (IAuthService svc, string user, string password)
{
// to be implemented
}
}
I've registered my interfaces and classes in LightInject:
var container = new LightInject.ServiceContainer();
container.Register<ICommand, Welcome>("welcome");
Now, I want to do something like this in my requests:
using (var scope = container.BeginScope())
{
// I need to tell my container how to resolve this dependency in case its needed
// but method below does not exist
scope.ResolverForScope<IUser>(()=>createIUser(request));
var command = container.GetInstance<ICommand>(command);
return command.Do();
}
What would be the correct way to do this in maintainable way with any DI container, considering that dependency chain might get quite long for complex methods?
EDIT I made my use case more clear (changed classes implementing IUser).
static class ScopedContainerExtensions
{
class ScopedContainer
{
Dictionary<Type, object> factories = new Dictionary<Type,object>();
public void Register<T>(Func<T> factory)
where T: class
{
factories.Add(typeof(T), new Lazy<T>(factory));
}
public T Resolve<T>()
{
return ((Lazy<T>)factories[typeof(T)]).Value;
}
}
public static void UseScopedContainerFor<Service>(this IServiceContainer container)
{
if (!container.CanGetInstance(typeof(ScopedContainer), ""))
{
container.Register<ScopedContainer>(new PerScopeLifetime());
}
container.Register<Service>(sf=>sf.GetInstance<ScopedContainer>().Resolve<Service>());
}
public static void ResolverForCurrentScope<T>(this IServiceContainer container, Func<IServiceFactory, T> factory)
where T : class
{
var scope = container.ScopeManagerProvider.GetScopeManager().CurrentScope;
container.GetInstance<ScopedStorage>().Register<T>(() =>
{
var instance = factory(container);
var disposable = instance as IDisposable;
if (disposable != null)
scope.TrackInstance(disposable);
return instance;
});
}
Registration:
container.UseScopedContainerFor<IUser>();
Usage in scope:
container.ResolverForCurrentScope<IUser>(fac => fac.GetInstance<OAuthUserFactory>().Create(fac.GetInstance<IOAuthService>(), Request));