sconzey sconzey - 1 month ago 15
C# Question

Windsor WCF Client for multiple endpoints

I have a service running on multiple different servers with very similar configurations. I want to be able to use Castle Windsor WCF Facility to generate a client for arbitrary endpoint addresses.

public class ServiceFactory {
public IService GetService(string hostName){
....
}
}


Now, I will know at compile time what all my services will be, so I can do this:

var container = new WindsorContainer();

// ...

container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
container.Register(Component.For<IService>().AsWcfClient(new DefaultClientModel()
{
Endpoint = WcfEndpoint.BoundTo(new NetTcpBinding()).At("net.tcp://hostname:port")
}).Named("hostname"));


And then do my ServiceFactory like this:

public class ServiceFactory
{
private readonly IWindsorContainer _container;

public ServiceFactory(IWindsorContainer container)
{
_container = container;
}

public IService GetService(string hostName)
{
return _container.Resolve<IService>(hostName);
}
}


But this is not robust against me forgetting to configure a particular endpoint. Is there a more elegant solution?

Answer

I had to spelunk through the WCF Facility source code, but YES! there is a more elegant solution:

The WCF facility integrates with the Typed Factory Facility and it can automatically build factory classes at runtime from a delegate or an interface.

First I make my service factory depend on a Func<IWcfEndpoint,IService>:

public class ServiceFactory {
    public ServiceFactory(Func<IWcfEndpoint, IService> resolveService){
        _resolveService = resolveService;
    }

    public IService GetService(string hostName){
        return _resolveService(WcfEndpoint.BoundTo(new NetTcpBinding()).At($"net.tcp://{hostname}:port"));
    }
}

Then when installing, I add the TypedFactoryFacility:

var container = new WindsorContainer();

// ...

container.AddFacility<TypedFactoryFacility>();
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
container.Register(
    Component.For<Func<IWcfEndpoint, IService>>().AsFactory(),
    Component.For<ServiceFactory>().ImplementedBy<ServiceFactory>());   

Windsor will then provide a ServiceFactory to anything that asks for one, and wire it up to produce an IService client from a hostname:

public class FooClass {
    public FooClass(ServiceFactory serviceFactory){
        var service = serviceFactory.GetService("localhost");
    }
}
Comments