题
我有一组通过桌面应用程序动态连接的 WCF Web 服务。
我的问题是 WCF 需要非常详细的配置设置才能工作。让 SSL 发挥作用涉及自定义设置。让 MTOM 或其他任何东西正常工作需要更多。你想要压缩吗?又来了...
WCF 确实很强大 - 您可以使用多种不同的方式进行连接,但所有方式似乎都涉及大量详细的配置。如果主机和客户端不完全匹配,您将很难破译错误。
我想让桌面应用程序更容易配置 - 最好是某种自动发现。桌面应用程序的用户应该只需输入 URL,它就会完成其余的工作。
有谁知道一个好方法来做到这一点?
我知道 Visual Studio 可以为您设置配置,但我希望桌面应用程序能够基于各种不同的服务器设置来完成此操作。
我知道 VS 的工具可以在外部使用,但我正在寻找桌面应用程序的用户不必是 WCF 专家。我知道微软故意让这个变得复杂。
有没有任何方法、机制、第三方库或任何东西可以自动发现 WCF 设置?
解决方案
有关端点的所有信息都可以在服务的元数据中获得,您可以编写一个客户端来探索服务的元数据并配置客户端。对于代码示例,您可以查看这个优秀的 墨西哥探索者 来自朱瓦尔·洛伊。
其他提示
谢谢,这是有用的代码(+1)。
不过,它有点混乱,有一些错误(例如,不应该进行区分大小写的检查),有大量我不需要的 UI 功能,并且重复了很多代码。
我从中获取了实际的发现机制,重新编写了它并且几乎让它工作了(连接,但需要一些技巧)。
首先是main方法使用的一些util函数:
/// <summary>If the url doesn't end with a WSDL query string append it</summary>
static string AddWsdlQueryStringIfMissing( string input )
{
return input.EndsWith( "?wsdl", StringComparison.OrdinalIgnoreCase ) ?
input : input + "?wsdl";
}
/// <summary>Imports the meta data from the specified location</summary>
static ServiceEndpointCollection GetEndpoints( BindingElement bindingElement, Uri address, MetadataExchangeClientMode mode )
{
CustomBinding binding = new CustomBinding( bindingElement );
MetadataSet metadata = new MetadataExchangeClient( binding ).GetMetadata( address, mode );
return new WsdlImporter( metadata ).ImportAllEndpoints();
}
然后是尝试不同方式连接并返回端点的方法:
public static ServiceEndpointCollection Discover( string url )
{
Uri address = new Uri( url );
ServiceEndpointCollection endpoints = null;
if ( string.Equals( address.Scheme, "http", StringComparison.OrdinalIgnoreCase ) )
{
var httpBindingElement = new HttpTransportBindingElement();
//Try the HTTP MEX Endpoint
try { endpoints = GetEndpoints( httpBindingElement, address, MetadataExchangeClientMode.MetadataExchange ); }
catch { }
//Try over HTTP-GET
if ( endpoints == null )
endpoints = GetEndpoints( httpBindingElement,
new Uri( AddWsdlQueryStringIfMissing( url ) ), MetadataExchangeClientMode.HttpGet );
}
else if ( string.Equals( address.Scheme, "https", StringComparison.OrdinalIgnoreCase ) )
{
var httpsBindingElement = new HttpsTransportBindingElement();
//Try the HTTPS MEX Endpoint
try { endpoints = GetEndpoints( httpsBindingElement, address, MetadataExchangeClientMode.MetadataExchange ); }
catch { }
//Try over HTTP-GET
if ( endpoints == null )
endpoints = GetEndpoints( httpsBindingElement,
new Uri( AddWsdlQueryStringIfMissing( url ) ), MetadataExchangeClientMode.HttpGet );
}
else if ( string.Equals( address.Scheme, "net.tcp", StringComparison.OrdinalIgnoreCase ) )
endpoints = GetEndpoints( new TcpTransportBindingElement(),
address, MetadataExchangeClientMode.MetadataExchange );
else if ( string.Equals( address.Scheme, "net.pipe", StringComparison.OrdinalIgnoreCase ) )
endpoints = GetEndpoints( new NamedPipeTransportBindingElement(),
address, MetadataExchangeClientMode.MetadataExchange );
return endpoints;
}
现在有另一种方法可以做到这一点,当我问原来的问题时,这是不可用的。Microsoft 现在支持 WCF 服务的 REST。
- 使用 REST 的缺点是您会丢失 WSDL。
- 好处是配置最少,您的 WCF 合约接口仍然可以工作!
您将需要一个新的参考 System.ServiceModel.Web
使用以下任一标记您的操作 WebInvoke
或者 WebGet
//get a user - note that this can be cached by IIS and proxies
[WebGet]
User GetUser(string id )
//post changes to a user
[WebInvoke]
void SaveUser(string id, User changes )
将这些添加到站点很容易 - 添加 .svc
文件:
<%@ServiceHost
Service="MyNamespace.MyServiceImplementationClass"
Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
工厂线告诉 ASP.net 如何激活端点 - 您根本不需要服务器端配置!
然后构建你的 ChannelFactory
几乎没有变化,只是您不再需要指定端点(或像我在其他答案中那样自动发现端点)
var cf = new WebChannelFactory<IMyContractInterface>();
var binding = new WebHttpBinding();
cf.Endpoint.Binding = binding;
cf.Endpoint.Address = new EndpointAddress(new Uri("mywebsite.com/myservice.svc"));
cf.Endpoint.Behaviors.Add(new WebHttpBehavior());
IMyContractInterface wcfClient = cf.CreateChannel();
var usr = wcfClient.GetUser("demouser");
// and so on...
请注意,我尚未指定或发现客户端配置 - 不需要本地配置!
另一个很大的好处是您可以轻松切换到 JSON 序列化 - 这允许 Java、ActionScript、Javascript、Silverlight 或任何其他可以轻松处理 JSON 和 REST 的东西使用相同的 WCF 服务。