I have a WCF client used in MVC application which can get data from multiple WCF services, the services are configured the same way and Implement the same Interface the only difference is the address of the exposed endpoint.
This is what I tried:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://service.com/Service")))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
The thing here is that IService
will always get data from http://service.com/Service
since the address is hardcoded somewhere in the Application_Start
method of the MVC application.
Then i tried using metadata:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://foo.com/Service")))
.SingleInstance().WithMetadata("name", "fooservice");
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://bar.com/Service")))
.SingleInstance().WithMetadata("name", "barservice");
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
But this way I will have to edit the code every time I want to add the same WCF service implemented on a different server.Instead I want to get the address from the database.
Is there any way I can change the address per service call or at least when the instance of the client is created.
Additional explanation:
Lets say I have five exact copies of a website each with it's own domain name and database I want to be able to do the following:
foreach(Provider provider in providers)
{
SetServiceAddress(provider.Address);//how can i do that
_service.GetData()
}
Under the assumptions that:
Then I'd probably solve it with a combination of lambdas and a small interface.
First, you'd want something that retrieves the address from your data store:
public interface IAddressReader
{
Uri GetAddress();
}
The implementation of that would read from the database (or environment, or XML config, or whatever).
Then I'd use that in my registrations:
builder
.RegisterType<MyDatabaseAddressReader>()
.As<IAddressReader>();
builder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
builder
.Register(c =>
{
var reader = c.Resolve<IAddressReader>();
var factory = c.Resolve<ChannelFactory<IService>();
var endpoint = new EndpointAddress(reader.GetAddress());
return factory.CreateChannel(endpoint);
})
.As<IService>()
.UseWcfSafeRelease();
That way you can just take in an IService
(or Func<IService>
) as a constructor parameter and your calling class won't know about Autofac, service location, or endpoints.
If the binding also changes, it gets a little more complicated. You probably don't want a brand new channel factory spun up for every channel, so you'd want to have some sort of caching mechanism where you:
If you can use cache dependencies on the settings, all the better, but not every configuration source supports that, so YMMV. I'd probably implement a custom module for that to encapsulate the logic, but I won't write all that out here.
If you want to set the endpoint just before the call each time, you can do this:
containerBuilder
.Register(c => new ChannelFactory<IService>(new BasicHttpBinding()))
.SingleInstance();
containerBuilder.Register((c, p) =>
{
var factory = c.Resolve<ChannelFactory<IService>>();
var endpointAddress = p.TypedAs<string>();
return factory.CreateChannel(new EndpointAddress(endpointAddress));
})
.As<IService>()
.UseWcfSafeRelease();
Then you inject this:
Func<string, IService> getService
Then call it like this:
string endpoint = getDataDependentEndpointFromSomewhere();
var service = getService(endpoint);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With