Implementing a Service Locator with injected variations of Generic Type

馋奶兔 提交于 2019-12-10 17:36:06

问题


I have the following:

public interface IConverter<TValue, TConverted>
{     
}

public interface IConverterProvider
{
    IConverter<TValue, TConverted> GetConverter<TValue, TConverted>();
}

With an example binding at setup:

Bind<IConverter<System.Int32, System.String>>().To<Int32ToStringConverter>();
Bind<IConverter<System.Guid, System.String>>().To<GuidToStringConverter>();

So I have a collection of fixed converters and no duplicate bindings.

[Question] My question is how do I go about implementing the IConverterProvider and injecting a dictionary of available bindings mapped to singletons? Or in other words, how to avoid the run-time service-locator pattern.

Currently I'm just using the NInject kernel to resolve each time, but I believe this an anti-pattern. What I would like is something like this:

public class ConverterProvider : IConverterProvider
{
    private Dictionary<Type, object> _converters;

    public ConverterProvider(Dictionary<Type, object> converters)
    {
        _converters = converters;
    }

    public IConverter<TValue, TConverted> GetConverter<TValue, TConverted>()
    {
        var fullTypeResolve = typeof (IConverter<,>).MakeGenericType(typeof (TValue), typeof (TConverted));

        return _converters.Where(x => x.Key == fullTypeResolve).Select(x=>x.Value).Cast<IConverter<TValue, TConverted>>().Single();
    }
}

But this effectively requires that I'm able to resolve and get a list of all IConverter<,> from the dependency injection kernel, and my previous attempts of doing this from NInject haven't been successful.


回答1:


This is supported by Ninject.Extensions.Factory

Bind<IConverter<System.Int32, System.String>>().To<Int32ToStringConverter>();
Bind<IConverter<System.Guid, System.String>>().To<GuidToStringConverter>();
Bind<IConverterProvider>().ToFactory();

No implementation required

Rename GetConverter to CreateConverter or another name not starting with Get




回答2:


I usually make a factory (or provider as you have here) with something like this:

public class ConverterFactory : IConverterFactory
{
    private readonly IResolutionRoot resolutionRoot;

    public ConverterFactory(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public IConverter<TValue, TConverted> CreateConverter()
    {
        Type converterType = typeof(IConverter<,>).MakeGenericType(typeof(TValue), typeof(TConverted));
        return this.resolutionRoot.Get(converterType);
    }
}

I know we're not really supposed to be using Ninject to create things at runtime but really sometimes it's unavoidable. To get the singleton behaviour, just define InSingletonScope() as normal.

If the types weren't generic then you can inject all implementations of the interface instead of the IResolutionRoot then pick one at runtime. I have a parser factory a bit like this:

public class ParserFactory : IParserFactory
{
    private readonly IEnumerable<IParser> parsers;

    public ParserFactory(IEnumerable<IParser> parsers)
    {
        this.parsers = parsers;
    }

    public IParser CreateParser(string someInput)
    {
        foreach (var parser in this.parsers)
        {
            if (parser.CanParse(someInput))
            {
                return parser;
            }
        }
    }
}

Ninject will automatically inject an IEnumerable containing all concrete implementations of the interface, so adding new implementations is as simple as adding a mapping. I don't know how to do that with generic types though since you can't say IEnumerable<IConverter<,>>.



来源:https://stackoverflow.com/questions/15108358/implementing-a-service-locator-with-injected-variations-of-generic-type

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