Resolve named registration dependency in Unity with runtime parameter

我怕爱的太早我们不能终老 提交于 2020-01-17 10:20:26

问题


I have a following problem. I register my components and initialize them in Unity like this (example is for a Console application):

public class SharePointBootstrapper : UnityBootstrapper
{
    ...

    public object Initialize(Type type, object parameter) =>
        Container.Resolve(type,
            new DependencyOverride<IClientContext>(Container.Resolve<IClientContext>(parameter.ToString())),
            new DependencyOverride<ITenantRepository>(Container.Resolve<ITenantRepository>(parameter.ToString())));

    public void RegisterComponents()
    {
        Container
            .RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString())
            .RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString())
            .RegisterType<ITenantRepository, DocumentDbTenantRepository>(SharePointClientContext.Online.ToString())
            .RegisterType<ITenantRepository, JsonTenantRepository>(SharePointClientContext.OnPremise.ToString());
    }
}

public enum SharePointClientContext
{
    Online,
    OnPremise
}

class Program
{
    static void Main(string[] args)
    {
        ...

        bootstrap.RegisterComponents();
        var bla = bootstrap.Initialize(typeof(ISharePointManager), SharePointClientContext.Online);
    }
}

So, I register my components in MVC, WCF, Console etc. once with RegisterComponents() and initialize them with Initialize().

My question is, if I want to initialize specific named registration at runtime, from e.g. user input, can it be done otherwise as the code presented (with InjectionFactory or similar)?

This code works fine, but I'm not happy with its implementation. I have a feeling that it could be written in RegisterComponents() instead of Initialize() so that it accepts a parameter of some type, but I don't know how to do it.

Or, is maybe my whole concept wrong? If so, what would you suggest? I need to resolve named registration from a parameter that is only known at runtime, regardless of the technology (MVC, WCF, Console, ...).

Thanks!


回答1:


Instead of doing different registrations, I would do different resolves.

Let's say that you need to inject IClientContext, but you want different implementations depending on a runtime parameter.

I wrote a similiar answer here. Instead of injecting IClientContext, you could inject IClientContextFactory, which would be responsible for returning the correct IClientContext. It's called Strategy Pattern.

public interface IClientContextFactory
{
    string Context { get; } // Add context to the interface.
}

public class SharePointOnlineClientContext : IClientContextFactory
{
    public string Context 
    {
        get 
        {
            return SharePointClientContext.Online.ToString(); 
        }
    }
}

// Factory for resolving IClientContext.
public class ClientContextFactory : IClientContextFactory
{
    public IEnumerable<IClientContext> _clientContexts;

    public Factory(IClientContext[] clientContexts)
    {
        _clientContexts = clientContexts;
    }

    public IClientContext GetClientContext(string parameter)
    {
        IClientContext clientContext = _clientContexts.FirstOrDefault(x => x.Context == parameter);
        return clientContext;
    }
}

Register them all, just as you did. But instead of injecting IClientContext you inject IClientContextFactor.

There also another solution where you use a Func-factory. Look at option 3, in this answer. One may argue that this is a wrapper for the service locator-pattern, but I'll leave that discussion for another time.

public class ClientContextFactory : IClientContextFactory
{
    private readonly Func<string, IClientContext> _createFunc;

    public Factory(Func<string, IClientContext> createFunc)
    {
        _createFunc = createFunc;
    }

    public IClientContext CreateClientContext(string writesTo)
    {
        return _createFunc(writesTo);
    }
}

And use named registrations:

container.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString());
container.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString());

container.RegisterType<IFactory, Factory>(
    new ContainerControlledLifetimeManager(), // Or any other lifetimemanager.
    new InjectionConstructor(
        new Func<string, IClientContext>(
            context => container.Resolve<IClientContext>(context));

Usage:

public class MyService
{
    public MyService(IClientContextFactory clientContextFactory)
    {
        _clientContextFactory = clientContextFactory;
    }

    public void DoStuff();
    {
        var myContext = SharePointClientContext.Online.ToString();
        IClientContextclientContext = _clientContextFactory.CreateClientContext(myContext);
    }
}


来源:https://stackoverflow.com/questions/38828552/resolve-named-registration-dependency-in-unity-with-runtime-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!