如何从LightCore ServiceLocator解析命名实例?

时间:2011-09-06 10:41:05

标签: c# dependency-injection inversion-of-control

最近,我正在玩IoC工具LightCore。由于上一版本不再允许任何命名实例,所以支持ServiceLocator模式(?)。

我需要ServiceLocator的一个重要功能是解析命名实例。我的第一个想法是覆盖DoGetInstance方法并实现我自己的代码。

我的上一个解决方法是通过它的typename

获取一个实例
_testInstanceKey = "My.Namespace.MyType, MyAssembly";
IMyType type = locator.GetInstance(typeof(IMyType), _testInstanceKey)

protected override object DoGetInstance(Type serviceType, string key)
{
    return _container.ResolveAll(serviceType)
             .Where(x => x.GetType() == Type.GetType(key,true,true))
             .FirstOrDefault();
}

这可以解决,但如果我硬连接我的类型名称,我需要一个ServiceLocator?

有没有人建议如何解决这个半生不熟的ServiceLocator?

2 个答案:

答案 0 :(得分:2)

您可以通过引入IMyTypeFactory接口并让应用程序依赖该接口来解决此问题:

// Factory interface:
public interface IMyTypeFactory
{
    IMyType GetByName(string name);
}

// Implementation in the composition root:
public class MyTypeFactory :
    Dictionary<string, Func<IRequestHandler>>, IMyTypeFactory
{
    public IMyType GetByName(string name) { return this[name](); }
}

// Registration
var factory = new MyTypeFactory
{
    { "foo", () => new MyType1() },
    { "bar", () => new MyType2() },
    { "boo", () => new MyType3() },
};

builder.Register<IMyTypeFactory>(c => factory);

正如我在评论中所说,试图摆脱使用Service Locator反模式。它将提高应用程序的可测试性和可维护性。使用依赖项注入(DI)模式时,不直接调用容器,也无法直接询问命名实例。在应用DI时,您必须更改配置容器的方式,或者需要在应用程序中应用正确的抽象(如IMyTypeFactory所示)。

答案 1 :(得分:0)

假设您无法更改框架调用定位器的方式,并假设框架使用公共服务定位器用作容器与其自身之间的接口,则可以将此功能添加到自定义IServiceLocator实现中:

// This adapter wraps the CSL LightCoreAdapter of LightCore itself.
public class LightCoreServiceLocatorAdapter : IServiceLocator
{
    private readonly LightCoreAdapter container;

    public LightCoreServiceLocatorAdapter(IContainer container)
    {
        // You need a reference to LightCore.CommonServiceLocator.dll.
        this.container = new LightCoreAdapter(container);
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
        return this.container.GetAllInstances<TService>();
    }

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    public TService GetInstance<TService>(string key)
    {
        if (key == null)
        {
            return this.container.GetInstance<TService>(null);
        }
        else
        {
           // This is custom logic
           this.container.GetInstance<INamedFactory<TService>>().GetByKey(key);
        }
    }

    public TService GetInstance<TService>()
    {
        return this.container.GetInstance<TService>();
    }

    public object GetInstance(Type serviceType, string key)
    {
        if (key == null)
        {
            return this.container.GetInstance(serviceType);
        }
        else
        {
            // This is custom logic
            var facType = typeof(INamedFactory<>).MakeGenericType(serviceType);
            var factory = (INamedFactory)this.container.GetInstance(facType);
            return factory.GetByKey(key);
        }
    }

    public object GetInstance(Type serviceType)
    {
        return this.container.GetInstance(serviceType);
    }

    object IServiceProvider.GetService(Type serviceType)
    {
        ((IServiceProvider)this.container).GetService(serviceType);
    }
}

此类使用以下两个自定义接口:

public interface INamedFactory
{
    object GetByKey(string key);
}

public interface INamedFactory<T> : INamedFactory
{
    T GetByKey(string key);
}

使用此自定义LightCoreServiceLocatorAdapter和这两个自定义接口,您可以创建自定义工厂,例如此通用工具:

public sealed class NamedDelegateFactory<T> : INamedFactory<T>
{
    private readonly Func<string, T> factory;

    public NamedDelegateFactory(Func<string, T> factory)
    {
        this.factory = factory;
    }

    public T GetByKey(string key)
    {
        return this.factory(key);
    }

    object INamedFactory.GetByKey(string key)
    {
        return this.factory(key);
    }
}

可以在容器中注册,如下所示:

var factory = new NamedDelegateFactory<IMyType>(key =>
{
    if (key == "Cool") return new MyType1();
    else return new MyType2();
});

var builder = new ContainerBuilder();
builder.Register<INamedFactory<IMyType>>(c => factory);

接下来,可以按如下方式创建LightCoreServiceLocatorAdapter

var adapter = new LightCoreServiceLocatorAdapter(builder.Build());

Microsoft.Practices.ServiceLocation.ServiceLocator
    .SetLocatorProvider(() => adapter);

您可以使用INamedFactory<T>界面注册所有命名实例,并使用包含在委托中的NamedDelegateFactory<T>或实现实现INamedFactory<T>的自定义类型。