01 WCF编程概述
SOA的优点
什么是WCF
WCF
是微软分布式应用程序开发的集大成者,它整合了.Net平台下所有的和分布式系统有关的技术,如
Enterprise Sevices
(COM+).Net Remoting(一种非常有弹性的扩展性框架)、
Web Service(ASMX)、
WSE3.0
MSMQ消息队列。
以通信(Communiation)范围而论,它可以
跨进程、跨机器、跨子网、企业网乃至于 Internet;
以宿主程序而论,可以以
ASP.NET,EXE,WPF,Windows Forms,NT Service,COM+作为宿主(Host)
WCF可以支持的协议包括TCP,
HTTP,跨进程以及自定义,
安全模式则包括SAML, Kerberos,X509,用户/密码,
自定义等多种标准与模式。
也就是说,在WCF框架下,开发基于SOA的分布式系统变得容易了,微软将所有与此相关的技术要素都包含在内,掌握了WCF,就相当于掌握了叩开SOA大门的钥匙
WCF与webService是什么关系
ASP.NET Web Service:
利用Soap协议对象在分布式环境之间的数据交互。
WSDL来发布服务接口相关的描写。
称为WSE系列:
ASP.NET Web Service的安全,功能,数据加密、解密,托管宿主等多方面的伸展,
WCF宿主
static void Main(string[] args) { var baseAddress = ConfigurationManager.AppSettings["baseAddress"]; ApplicationService.Initialize(); MongoDBBootstrapper.Bootstrap(); using (ServiceHost orderService = new ServiceHost(typeof(OrderService), new Uri(string.Format("{0}/OrderService.svc", baseAddress)))) using (ServiceHost productService = new ServiceHost(typeof(ProductService), new Uri(string.Format("{0}/ProductService.svc", baseAddress)))) using (ServiceHost userService = new ServiceHost(typeof(UserService), new Uri(string.Format("{0}/UserService.svc", baseAddress)))) { orderService.Open(); productService.Open(); userService.Open(); Console.WriteLine("Byteart Retail Services started at: {0}.", DateTime.Now); Console.ReadLine(); Console.WriteLine("Byteart Retail Services stopped at: {0}.", DateTime.Now); orderService.Close(); productService.Close(); userService.Close(); } }
<appSettings> <add key="baseAddress" value="http://localhost:8559"/> </appSettings>
服务端配置:
<system.serviceModel> <extensions> <behaviorExtensions> <add name="unity" type="ByteartRetail.Infrastructure.UnityExtensions.UnityBehaviorExtensionElement, ByteartRetail.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> <unity operationContextEnabled="true" instanceContextEnabled="true" contextChannelEnabled="true" serviceHostBaseEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>
另一种配置,
namespace Keasy5.WCF.WCFHost { class Program { static void Main(string[] args) { using (ServiceHost serviceHostPerCall = new ServiceHost(typeof(ServicePerCall))) using (ServiceHost serviceHostPerSession = new ServiceHost(typeof(ServicePerSession))) using (ServiceHost serviceHostSingle = new ServiceHost(typeof(ServiceSingle))) { serviceHostPerCall.Open(); serviceHostPerSession.Open(); serviceHostSingle.Open(); Console.WriteLine("所有服务已经启动: {0}.", DateTime.Now); Console.ReadLine(); Console.WriteLine("所有服务已经关闭: {0}.", DateTime.Now); serviceHostPerCall.Close(); serviceHostPerSession.Close(); serviceHostSingle.Close(); } } } }
对应的配置文件:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <compilation debug="true" /> </system.web> <system.serviceModel> <services> <service name="Keasy5.WCF.WCFService.ServicePerCall" behaviorConfiguration="textserviceBehaviors"> <host> <baseAddresses> <add baseAddress = "http://localhost:5833/Keasy5.WCF.WCFService/ServicePerCall/" /> </baseAddresses> </host> <endpoint name="ServicePerCall" address="" binding="basicHttpBinding" contract="Keasy5.WCF.WCFService.IService1"></endpoint> </service> <service name="Keasy5.WCF.WCFService.ServicePerSession" behaviorConfiguration="textserviceBehaviors"> <host> <baseAddresses> <add baseAddress = "http://localhost:5833/Keasy5.WCF.WCFService/ServicePerSession/" /> </baseAddresses> </host> <endpoint name="" address="" binding="basicHttpBinding" contract="Keasy5.WCF.WCFService.IService1"></endpoint> </service> <service name="Keasy5.WCF.WCFService.ServiceSingle" behaviorConfiguration="textserviceBehaviors"> <host> <baseAddresses> <add baseAddress = "http://localhost:5833/Keasy5.WCF.WCFService/ServiceSingle/" /> </baseAddresses> </host> <endpoint name="ServiceSingle" address="" binding="basicHttpBinding" contract="Keasy5.WCF.WCFService.IService1"></endpoint> </service> </services> <behaviors> <serviceBehaviors > <behavior name="textserviceBehaviors"> <serviceMetadata httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="True" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
客户端调用
获取WCF服务的一个封装类:
ServiceProxy.cs
using System.ServiceModel; namespace ByteartRetail.Infrastructure.Communication { /// <summary> /// 表示用于调用WCF服务的客户端服务代理类型。 /// </summary> /// <typeparam name="T">需要调用的服务契约类型。</typeparam> public sealed class ServiceProxy<T> : DisposableObject where T : class, IApplicationServiceContract { #region Private Fields private T client = null; private static readonly object sync = new object(); #endregion #region Protected Methods protected override void Dispose(bool disposing) { if (disposing) lock (sync) { Close(); } } #endregion #region Public Properties /// <summary> /// 获取调用WCF服务的通道。 /// </summary> public T Channel { get { lock (sync) { if (client != null) { var state = (client as IClientChannel).State; if (state == CommunicationState.Closed) client = null; else return client; } var factory = ChannelFactoryManager.Instance.GetFactory<T>(); client = factory.CreateChannel(); (client as IClientChannel).Open(); return client; } } } #endregion #region Public Methods /// <summary> /// 关闭并断开客户端通道(Client Channel)。 /// </summary> /// <remarks> /// 如果使用using语句对ServiceProxy进行了包裹,那么当程序执行点离开using的 /// 覆盖范围时,Close方法会被自动调用,此时客户端无需显式调用Close方法。反之 /// 如果没有使用using语句,那么则需要显式调用Close方法。 /// </remarks> public void Close() { if (client != null) ((IClientChannel)client).Close(); } #endregion } }
ChannelFactoryManager.cs
using System; using System.Collections.Generic; using System.ServiceModel; namespace ByteartRetail.Infrastructure.Communication { /// <summary> /// 表示Channel Factory管理器。 /// </summary> internal sealed class ChannelFactoryManager : DisposableObject { #region Private Fields private static readonly Dictionary<Type, ChannelFactory> factories = new Dictionary<Type, ChannelFactory>(); private static readonly object sync = new object(); private static readonly ChannelFactoryManager instance = new ChannelFactoryManager(); #endregion #region Ctor static ChannelFactoryManager() { } private ChannelFactoryManager() { } #endregion #region Public Properties /// <summary> /// 获取<c>ChannelFactoryManager</c>的单件(Singleton)实例。 /// </summary> public static ChannelFactoryManager Instance { get { return instance; } } #endregion #region Protected Methods protected override void Dispose(bool disposing) { if (disposing) { lock (sync) { foreach (Type type in factories.Keys) { ChannelFactory factory = factories[type]; try { factory.Close(); continue; } catch { factory.Abort(); continue; } } factories.Clear(); } } } #endregion #region Public Methods /// <summary> /// 获取与指定服务契约类型相关的Channel Factory实例。 /// </summary> /// <typeparam name="T">服务契约的类型。</typeparam> /// <returns>与指定服务契约类型相关的Channel Factory实例。</returns> public ChannelFactory<T> GetFactory<T>() where T : class, IApplicationServiceContract { lock (sync) { ChannelFactory factory = null; if (!factories.TryGetValue(typeof(T), out factory)) { factory = new ChannelFactory<T>(typeof(T).Name); factory.Open(); factories.Add(typeof(T), factory); } return factory as ChannelFactory<T>; } } #endregion } }
客户端调用:
using (ServiceProxy<IProductService> proxy = new ServiceProxy<IProductService>())
{
var categories = proxy.Channel.GetCategories(QuerySpec.Empty);
return View(categories);
}
客户端配置:
注意到ChannelFactoryManager类中创建工厂的方法:
public ChannelFactory<T> GetFactory<T>()
{
。。。。 factory = new ChannelFactory<T>(typeof(T).Name);
注意到ChannelFactoryManager类中创建工厂的方法,使用的是类型的名称typeof(T).Name)作为节点的配置名。
ChannelFactory<T>构造函数查看参数说明:
// // 摘要: // 使用指定的终结点配置名称初始化 System.ServiceModel.ChannelFactory<TChannel> 类的新实例。 // // 参数: // endpointConfigurationName: // 用于终结点的配置名称。 // // 异常: // System.ArgumentNullException: // endpointConfigurationName 为 null。 public ChannelFactory(string endpointConfigurationName);
故,客户端的配置EndPointd配置节点名要使用要用类型的名称,
<endpoint name="IProductService" 。。。
例如:
<endpoint name="IProductService" address="http://localhost:8558/ProductService.svc" contract="ByteartRetail.ServiceContracts.IProductService" binding="basicHttpBinding" bindingConfiguration="ServiceProxyBinding" />
客户端的配置:
<system.serviceModel> <bindings> <basicHttpBinding> <binding name="ServiceProxyBinding" sendTimeout="00:10:00" receiveTimeout="00:10:00" closeTimeout="00:10:00" maxReceivedMessageSize="134217728" maxBufferSize="134217728"> <readerQuotas maxStringContentLength="134217728" /> </binding> </basicHttpBinding> </bindings> <client> <endpoint name="IProductService" address="http://localhost:8558/ProductService.svc" contract="ByteartRetail.ServiceContracts.IProductService" binding="basicHttpBinding" bindingConfiguration="ServiceProxyBinding" /> <endpoint name="IOrderService" address="http://localhost:8558/OrderService.svc" contract="ByteartRetail.ServiceContracts.IOrderService" binding="basicHttpBinding" bindingConfiguration="ServiceProxyBinding" /> <endpoint name="IUserService" address="http://localhost:8558/UserService.svc" contract="ByteartRetail.ServiceContracts.IUserService" binding="basicHttpBinding" bindingConfiguration="ServiceProxyBinding" /> <endpoint name="IPostbackService" address="http://localhost:8558/PostbackService.svc" contract="ByteartRetail.ServiceContracts.IPostbackService" binding="basicHttpBinding" bindingConfiguration="ServiceProxyBinding" /> </client> </system.serviceModel>