我正在使用具有如下设置结构的第三方库:
IEngine engine = /* singleton provided elsewhere */
var server = new FooServer();
server.AddService("Data1", () => new Data1(engine));
server.AddService("Data2", () => new Data2(engine));
server.Start();
...
server.Dispose();
(lambda本质上是一个工厂方法;只要它想要一个新实例用于它自己的目的,它就会在内部调用它。)
除了更复杂的是,我不是直接添加服务,而是使用反射来查找和注册它们,这样它们只需要被定义为工作而不需要明确列出。最初我想完全自包含,但是基于反射类型构建通用lambda方法似乎太复杂了,所以目前我已经用每种类型提供的Register
方法解决了:
class Data1 : DataProvider
{
public static void Register(FooServer server, IEngine engine)
{
server.AddService("Data1", () => new Data1(engine));
}
... (constructor, Dispose, other stuff)
}
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var method = type.GetMethod("Register", new[] { typeof(FooServer), typeof(IEngine) });
if (method != null)
{
method.Invoke(null, new object[] { server, engine });
}
// a more ideal approach would be to construct the needed lambda and call
// AddService directly instead of using Register, but my brain fails me.
}
server.Start();
...
server.Dispose();
毋庸置疑,这有点难看,我确信这是一个更好的方法。另一件事是,我已经使用Castle Windsor来创建IEngine
以及其他一些使用它的东西,我想知道如何更好地与它集成。 (目前我只是Resolve
引擎在此代码需要它的位置 - 它是一个单身,所以生命周期并不棘手。)
我真正喜欢的是一种使用方法参数或构造函数注入的方法,以便每个DataProvider
可以根据其实际依赖性(而不是所有依赖项的并集)具有不同的参数集就像你在Windsor的控制下所做的一切一样。但同样,我不知道从哪里开始。我还没有真正使用温莎,除了基础知识。
请注意,FooServer
,DataProvider
和AddService<T>(string name, Func<T> factory) where T: DataProvider
方法都在外部代码中,我无法更改它们。其余的(包括引擎)是我的代码。再次请注意,我根本不在代码中创建Data1
实例,只是一个工厂lambda,告诉外部服务器如何在需要时创建它们。
对qujck's answer进行一些必要的修改后,会产生以下代码:
var container = ...;
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var t = type; // necessary due to the lambda capturing
container.Register(Component.For(t).LifestyleTransient());
server.AddService(t.Name, () => {
var service = (DataProvider) container.Resolve(t);
service.Closed += (s, e) => container.Release(service);
return service;
});
}
server.Start();
...
server.Dispose();
这表现得很好,尽管我仍然对进一步改进它的方法感兴趣。 (我很好奇是否有某种方法可以使用Castle自己的Classes.FromAssembly...
等语法来整理服务的发现和注册,但是没有多少运气能够解决这个问题。)< / p>
答案 0 :(得分:2)
您可以定义从容器中解析的lambda。这提供了在一个地方(容器)管理所有服务及其相关生命周期的好处。
您需要一些方法来确定每个注册的名称 - 在示例中,我已将每个服务注册为类型的名称:
[Fact]
public void Configure1()
{
IWindsorContainer container = new WindsorContainer();
var server = new MockFooServer();
container.Register(Component.For<IEngine>().ImplementedBy<Engine>());
foreach (Type type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
container.Register(Component.For(type));
server.AddService(type.Name, () => container.Resolve(type) as DataProvider);
}
var service1 = server.services[typeof(Service1).Name]();
Assert.IsType<Service1>(service1);
}
使用模拟FooServer
进行测试:
public class MockFooServer
{
public Dictionary<string, Func<DataProvider>> services =
new Dictionary<string, Func<DataProvider>>();
public void AddService<T>(string key, Func<T> factory) where T : DataProvider
{
this.services.Add(key, factory as Func<DataProvider>);
}
}