我有一个在MVC应用程序中使用的WCF客户端,它可以从多个WCF服务获取数据,服务以相同的方式配置并实现相同的接口,唯一的区别是暴露端点的地址。
这就是我的尝试:
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress("http://service.com/Service")))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
这里的事情是IService
将始终从http://service.com/Service
获取数据,因为地址是在MVC应用程序的Application_Start
方法中的某处硬编码。
然后我尝试使用元数据:
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();
但是这样我每次想要添加相同的WCF服务时都必须编辑代码 在不同的服务器上实现。相反,我想从数据库中获取地址。
有什么方法可以更改每次服务调用的地址,或者至少在创建客户端实例时。
补充说明:
让我们说我有一个网站的五个精确副本,每个网站都有自己的域名和数据库,我希望能够做到以下几点:
foreach(Provider provider in providers)
{
SetServiceAddress(provider.Address);//how can i do that
_service.GetData()
}
答案 0 :(得分:5)
根据以下假设:
然后我可能会用lambda和一个小接口来解决它。
首先,您需要从数据存储中检索地址的内容:
public interface IAddressReader
{
Uri GetAddress();
}
它的实现将从数据库(或环境,或XML配置,或其他)读取。
然后我会在我的注册中使用它:
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();
这样您就可以使用IService
(或Func<IService>
)作为构造函数参数,而您的调用类将不会了解Autofac,服务位置或端点。
如果绑定也发生变化,则会变得更复杂。您可能不希望为每个频道启动一个全新的频道工厂,所以您希望有一些你在哪里的缓存机制:
如果您可以在设置上使用缓存依赖项,那么更好,但不是每个配置源都支持,所以YMMV。我可能会实现一个自定义模块来封装逻辑,但我不会在这里写出所有这些。
答案 1 :(得分:1)
如果您想在每次呼叫之前设置端点,可以执行以下操作:
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();
然后你注入这个:
Func<string, IService> getService
然后这样称呼:
string endpoint = getDataDependentEndpointFromSomewhere();
var service = getService(endpoint);
答案 2 :(得分:0)
我有一个在多个站点上运行的服务,在启动时,应用程序需要确定它在哪个站点上运行。它使用启动参数来实现,并且基于此,可以在属性或方法中动态设置端点地址,如GetEndPointAddressForService()。
在您的情况下,您似乎需要连续在不同的站点呼叫n个服务。您绝对可以在数据库或磁盘上的简单配置文件中配置它们,在启动时加载服务定义(包括其端点地址),将它们保存在列表中,并在从所有现有服务器收集数据时执行操作。
逻辑的关键部分在代码的以下部分:
new EndpointAddress("http://bar.com/Service")
做一个
foreach (ServiceDefinition sd in ServiceDefinitions)
{
builder.Register(c => new ChannelFactory<IService>(
new BasicHttpBinding(),
new EndpointAddress(sd.EndPointAddress)))
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ChannelFactory<IService>>().CreateChannel())
.UseWcfSafeRelease();
GoGetTheData();
}
答案 3 :(得分:0)
最后我使用了以下实现:
在应用程序启动时,我注册没有端点地址的ChannelFactory
类型。
我使用命名参数来注册客户端,以便我能够在以后实际调用服务时分配地址。
builder.RegisterType<ChannelFactory<IService>>(new BasicHttpBinding())
.SingleInstance();
builder.Register((c, p) => c.Resolve<ChannelFactory<IService>>().CreateChannel(p.Named<EndpointAddress>("address")))
.UseWcfSafeRelease();
然后我在运行时使用服务客户端,如下所示:
public Data GetData(string url)
{
EndpointAddress address = new EndpointAddress(url);
NamedParameter parameter = new NamedParameter("address", address);
var service = _autofacContainer.Resolve<IService>(parameter);//this is what I have been looking for
Response response = service.GetData();
return CreateDataFromResponse(response);
}
这样我可以为数据库中的每个地址调用GetData
方法。而且我将能够在运行时添加更多地址而无需编写代码或配置。