实现具有注入的通用类型变体的服务定位器

时间:2013-02-27 09:23:19

标签: c# generics ninject service-locator

我有以下内容:

public interface IConverter<TValue, TConverted>
{     
}

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

在设置时使用示例绑定:

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

所以我有一组固定的转换器,没有重复的绑定。

[问题] 我的问题是如何实现IConverterProvider并注入映射到单例的可用绑定字典?或者换句话说,如何避免运行时服务定位器模式。

目前我只是使用NInject内核来解决每次问题,但我相信这是一种反模式。我想要的是这样的:

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();
    }
}

但这有效地要求我能够解析并获得所有IConverter&lt;,&gt;的列表。从依赖注入内核,我之前从NInject做这件事的尝试都没有成功。

2 个答案:

答案 0 :(得分:1)

Ninject.Extensions.Factory

支持此功能
Bind<IConverter<System.Int32, System.String>>().To<Int32ToStringConverter>();
Bind<IConverter<System.Guid, System.String>>().To<GuidToStringConverter>();
Bind<IConverterProvider>().ToFactory();

无需实施

GetConverter重命名为CreateConverter或其他不以Get

开头的名称

答案 1 :(得分:0)

我通常用这样的东西建立一个工厂(或你在这里的提供者):

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);
    }
}

我知道我们并不是真的应该使用Ninject在运行时创建东西,但实际上有时它是不可避免的。要获得单例行为,只需将InSingletonScope()定义为正常。

如果类型不是通用的,那么你可以注入接口的所有实现而不是IResolutionRoot,然后在运行时选择一个。我有一个像这样的解析器工厂:

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将自动注入包含接口所有具体实现的IEnumerable,因此添加新实现就像添加映射一样简单。我不知道如何使用泛型类型,因为你不能说IEnumerable<IConverter<,>>