使WCF易于配置
我有一组通过桌面应用程序动态连接的WCF Web服务。
我的问题是WCF需要工作的真正详细的配置设置。使SSL生效需要进行自定义设置。使MTOM或者其他任何东西都需要工作。我们要压缩吗?我们重新来过吧...
WCF确实非常强大,我们可以使用多种不同的方式进行连接,但是所有这些似乎都涉及许多详细的配置。如果主机和客户端不完全匹配,我们将很难破译错误。
我想使桌面应用程序更容易配置,理想情况下是某种自动发现。桌面应用程序的用户应该只能够输入URL,其余的就由它来完成。
有人知道这样做的好方法吗?
我知道Visual Studio可以为我们设置配置,但是我希望桌面应用程序能够基于各种不同的服务器设置来进行配置。
我知道VS的工具可以在外部使用,但我希望桌面应用程序的用户不必成为WCF专家。我知道MS故意使此操作变得过于复杂。
有什么方法,机制,第三者库或者其他任何东西可以自动发现WCF设置?
解决方案
回答
服务的元数据中提供了有关端点的所有信息,我们可以编写客户端,以浏览服务的元数据并配置客户端。有关代码示例,我们可以查看Juval Lowy提供的出色的Mex Explorer。
回答
谢谢,那是有用的代码(+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现在支持REST for WCF服务。
- 使用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服务。