最近,我正在玩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?
答案 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>
的自定义类型。