温莎城堡 - 如何通过名字解决?

时间:2014-02-13 11:05:58

标签: castle-windsor

我的应用程序使用“SignalR”客户端/服务器通信框架。如果您不熟悉它,服务器端应用程序通常包含一个或多个“集线器”类(类似于asmx Web服务),每个类提供可由客户端调用的方法。在启动期间,客户端需要首先创建连接,然后为每个需要与之通信的集线器创建“代理”,例如: -

var hubConnection = new HubConnection("http://...");
var fooHubProxy = hubConnection.CreateHubProxy("FooHub");
var barHubProxy = hubConnection.CreateHubProxy("BarHub");
...etc...

传递给CreateHubProxy()的字符串参数是服务器端集线器类的名称。方法返回类型为IHubProxy

感觉我应该能够在这里使用温莎,但我很难找到解决方案。我的第一个想法是实例化集线器代理并使用Windsor(按名称)注册这些实例,例如。

var fooHubProxy = hubConnection.CreateHubProxy("FooHub");
container.Register(Component.For<IHubProxy>().Instance(fooHubProxy).LifestyleSingleton().Named("FooHub"));
...etc...

问题是当一个类需要一个集线器代理时,按名称解析它的唯一方法是使用服务定位器模式,这是不推荐的。 Windsor的其他功能(例如打字的工厂等)在这里可能有用吗?

修改

我刚刚找到Windsor的.UsingFactoryMethod,我想知道这是否有效,以简化集线器注册:

container.Register(Component.For<IHubProxy>()
                   .UsingFactoryMethod((kernel, context) => hubConnection.CreateHubProxy("FooHub"))
                   .LifestyleSingleton()
                   .Named("FooHub"));

我想我仍然有如何通过名字解决的问题。

3 个答案:

答案 0 :(得分:4)

两年后,但是对于其他人来说,我也有一个更优雅的解决方案。 可以使用TypedFactory工具并根据您的需要进行调整,如here。 首先创建工厂界面(仅!不需要实际实现,城堡将负责):

public interface IHubProxyFactory
{
   IHubProxy GetProxy(string proxyName);
}

现在我们需要一个扩展默认类型facotory的类,并从输入(proxyName)中检索组件的名称:

class NamedTypeFactory : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        string componentName = null;
        if (arguments!= null && arguments.Length > 0)
        {
            componentName = arguments[0] as string;
        }

        if (string.IsNullOrEmpty(componentName))
            componentName = base.GetComponentName(method, arguments);

        return componentName;
    }
}

然后使用castle注册工厂并指定将使用NamedTypeFactory:

Component.For<IHubProxyFactory>().AsFactory(new NamedTypeFactory())

现在每个类都可以在其构造函数中获取工厂接口:

public class SomeClass
{
    private IHubProxy _fooHub;
    private IHubProxy _barHub;

    public SomeClass(IHubProxyFactory hubProxyFactory)
    {
        _fooHub = hubProxyFactory.GetProxy("FooHub");
        _barHub = hubProxyFactory.GetProxy("BarHub");
    }
}

答案 1 :(得分:2)

好的,我想我找到了一个可能的解决方案,部分使用了详细的here方法,该方法显示了如何向温莎注册Func<>

首先,我注册一个使用容器按名称解析的委托(Func&lt;&gt;): -

Container.Register(Component.For<Func<string, IHubProxy>>()
                   .Instance(name => Container.Resolve<IHubProxy>(name))
                   .LifestyleSingleton());

将此视为IHubProxy“工厂”。

接下来,我按照原始问题详细说明了我的中心代理: -

container.Register(Component.For<IHubProxy>()
               .UsingFactoryMethod((kernel, context) => hubConnection.CreateHubProxy("FooHub"))
               .LifestyleSingleton()
               .Named("FooHub"));
container.Register(Component.For<IHubProxy>()
               .UsingFactoryMethod((kernel, context) => hubConnection.CreateHubProxy("BarHub"))
               .LifestyleSingleton()
               .Named("BarHub"));

以下是需要集线器代理实例的类的示例: -

public class SomeClass
{
    private IHubProxy _fooHub;
    private IHubProxy _barHub;

    public SomeClass(Func<string, IHubProxy> hubProxyFactory)
    {
        _fooHub = hubProxyFactory("FooHub");
        _barHub = hubProxyFactory("BarHub");
    }
}

目前尚未尝试,但看起来很有希望。这是一个聪明的解决方案,但注入Func&lt;&gt;感觉有点hacky,所以我仍然希望听到其他可能解决我问题的方法。

答案 2 :(得分:1)

我刚用你的方法。我使用的是打字工厂。优点是我的集线器具有类型安全性。注册集线器是一样的。其余的有点不同,但技术上是一样的。

IServiceFactory { 
   IHubProxy GetFooHub();
   IHubProxy GetBarHub();
}

注册:

Container.AddFacility<TypedFactoryFacility>();
Container.Register(Component.For<IServiceFactory>().AsFactory());

用法:

public class SomeClass
{
    private IHubProxy _fooHub;
    private IHubProxy _barHub;

    public SomeClass(IServiceFactry hubProxyFactory)
    {
        _fooHub = hubProxyFactory.GetFooHub();
        _barHub = hubProxyFactory.GetBarHub();
    }
}

顺便说一下。 Factory.Get"Name"()按名称解析。