Resolve object using DI container with object instance

3.8k Views Asked by At

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).

2

There are 2 best solutions below

3
On BEST ANSWER
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));
1
On

It might be developed via using the Factory pattern.

With this approach, you might be able to get an instance of the specific user with a Factory to provide instances for each concrete class.

Using explicit service registration:

var container = new LightInject.ServiceContainer();
//register your command passing a user instance
container.Register<ICommand>(factory => new Welcome(factory.GetUser<IUser>(request)));

using (var scope = container.BeginScope())
{        
    var command = (ICommand)container.GetInstance<ICommand>();
    return command.Do();
}

I just referred to LightInject web page. There is a chapter called "Dependency Constructors" for further information. http://www.lightinject.net/#toc16

Hope it helps