WCF很早就出现了,然而我感受到能够让新手重点去学习WCF而不是WebService是最近两年。我相信大部分人初步了解WCF的时候会很痛苦,尤其是生成代理类,以及配置的问题。我本人其实比较讨厌配置编程,但喜欢轻量配置,因此也一直研究如何自己定义配置节点,去让WCF服务识别,然后重量级ABC操作由编码来完成。下面,我将会提供一个我本人用于学习其他知识需要使用WCF时的开发模式,另外说明,为了更好的说明,我将提供的都是精简版本,并不适合用于测试生产的例子。
1.定义适合自己的配置
1 public class WCFServiceElement : ConfigurationElement 2 { 3 [ConfigurationProperty(name: "ServiceName", IsRequired = false)] 4 public string ServiceName 5 { 6 get 7 { 8 return (base["ServiceName"] as string); 9 } 10 set 11 { 12 base["ServiceName"] = value; 13 } 14 } 15 16 [ConfigurationProperty(name: "ServiceAssembly", IsRequired = false)] 17 public string ServiceAssembly 18 { 19 get 20 { 21 return (base["ServiceAssembly"] as string); 22 } 23 set 24 { 25 base["ServiceAssembly"] = value; 26 } 27 } 28 29 [ConfigurationProperty(name: "InterfaceServiceName", IsRequired = false)] 30 public string InterfaceServiceName 31 { 32 get 33 { 34 return (base["InterfaceServiceName"] as string); 35 } 36 set 37 { 38 base["InterfaceServiceName"] = value; 39 } 40 } 41 42 [ConfigurationProperty(name: "InterfaceServiceAssembly", IsRequired = false)] 43 public string InterfaceServiceAssembly 44 { 45 get 46 { 47 return (base["InterfaceServiceAssembly"] as string); 48 } 49 set 50 { 51 base["InterfaceServiceAssembly"] = value; 52 } 53 } 54 55 [ConfigurationProperty(name: "IPAddress", IsRequired = false)] 56 public string IPAddress 57 { 58 get 59 { 60 return (base["IPAddress"] as string); 61 } 62 set 63 { 64 base["IPAddress"] = value; 65 } 66 } 67 68 [ConfigurationProperty(name: "Binding", IsRequired = false)] 69 public string Binding 70 { 71 get 72 { 73 return (base["Binding"] as string); 74 } 75 set 76 { 77 base["Binding"] = value; 78 } 79 } 80 }
1 public class WCFServicesElementCollection : ConfigurationElementCollection 2 { 3 protected override ConfigurationElement CreateNewElement() 4 { 5 ConfigurationElement _element = new WCFServiceElement(); 6 return _element; 7 } 8 9 protected override object GetElementKey(ConfigurationElement element) 10 { 11 WCFServiceElement _element = (WCFServiceElement)element; 12 return _element.ServiceName; 13 } 14 15 protected override string ElementName 16 { 17 get 18 { 19 return "WCFService"; 20 } 21 } 22 23 public override ConfigurationElementCollectionType CollectionType 24 { 25 get 26 { 27 return ConfigurationElementCollectionType.BasicMap; 28 } 29 } 30 }
1 public class WCFServiceSection : ConfigurationSection 2 { 3 [ConfigurationProperty(name: "", IsDefaultCollection = true, IsRequired = false)] 4 public WCFServicesElementCollection WCFServices 5 { 6 get 7 { 8 return (base[""] as WCFServicesElementCollection); 9 } 10 set 11 { 12 base[""] = value; 13 } 14 } 15 }
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="WCFServices" type="Basic.Configuration.WCFServiceSection,Basic.Data"/> 5 </configSections> 6 <WCFServices> 7 <WCFService ServiceName="AppManagementImplementation" 8 ServiceAssembly="Service.BackgroundManagement.Implementation" 9 InterfaceServiceName="IAppManagement" 10 InterfaceServiceAssembly="Service.BackgroundManagement.Interface" 11 IPAddress="http://192.168.1.4:8085/AppManagementService" 12 Binding="WSHttpBinding"> 13 </WCFService> 14 </WCFServices> 15 </configuration>
这里注意的是configSections一定要放在第一行,这个倒是听让人无语的。当然配置可以用自己希望方式配置,我这里提供一个简单的思路。
2.定义适合自己的WCFServerConfigurationManagement
1 public abstract class WCFServerConfigurationManagement 2 { 3 private static HybridDictionary _Collection = new HybridDictionary(); 4 5 public static void Register() 6 { 7 WCFServiceSection section = ConfigurationManager.GetSection("WCFServices") as WCFServiceSection; 8 if (null == section) 9 { 10 throw new NullReferenceException("未能识别WCFServices,请确认configSections配置节点"); 11 } 12 13 _Collection.Clear(); 14 15 foreach (WCFServiceElement element in section.WCFServices) 16 { 17 Assembly assemblyInterfaceService = Assembly.Load(element.InterfaceServiceAssembly); 18 Assembly assemblyService = Assembly.Load(element.ServiceAssembly); 19 20 Type typeInterface = assemblyInterfaceService.ExportedTypes.FirstOrDefault(type => type.Name.Equals(element.InterfaceServiceName)); 21 Type typeImplement = assemblyService.ExportedTypes.FirstOrDefault(type => type.Name.Equals(element.ServiceName)); 22 23 if (null == typeInterface && null == typeImplement) 24 { 25 throw new NullReferenceException("无法加载服务"); 26 } 27 28 ServiceHost _NewHost = new ServiceHost(typeImplement); 29 Binding binding = CreateBinding(element.Binding); 30 _NewHost.AddServiceEndpoint(typeInterface, binding, element.IPAddress); 31 32 _NewHost.Opened += (sender, e) => 33 { 34 #if DEBUG 35 Console.WriteLine($"服务:{element.ServiceName} 已经开启."); 36 #endif 37 }; 38 _NewHost.Open(); 39 } 40 } 41 42 public static Binding CreateBinding(string bindingType) 43 { 44 Binding _Binding = default(Binding); 45 switch (bindingType) 46 { 47 case "BasicHttpBinding": 48 _Binding = new BasicHttpBinding(); 49 break; 50 case "WSHttpBinding": 51 _Binding = new WSHttpBinding(); 52 break; 53 case "WS2007HttpBinding": 54 _Binding = new WS2007HttpBinding(); 55 break; 56 default: 57 throw new ArgumentNullException("根据接口名称无法匹配绑定类型"); 58 } 59 60 return _Binding; 61 } 62 }
这里主要用来根据配置动态配置WCF ServiceHost 和 Binding 的.然后再您的Custom Host 里面调用如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 WCFServerConfigurationManagement.Register(); 6 7 Console.ReadLine(); 8 } 9 }
3.定义适合自己的WCFClientConfigurationManagement
1 public abstract class WCFClientConfigurationManagement 2 { 3 private static HybridDictionary _Collection = new HybridDictionary(); 4 5 public static void Register() 6 { 7 WCFServiceSection section = ConfigurationManager.GetSection("WCFServices") as WCFServiceSection; 8 if (null == section) 9 { 10 throw new NullReferenceException("未能识别WCFServices,请确认configSections配置节点"); 11 } 12 13 _Collection.Clear(); 14 15 foreach (WCFServiceElement element in section.WCFServices) 16 { 17 _Collection.Add(element.InterfaceServiceName , element); 18 } 19 } 20 21 public static Binding CreateBinding(string serviceName) 22 { 23 Binding _Binding = default(Binding); 24 WCFServiceElement element = _Collection[serviceName] as WCFServiceElement; 25 if (null == element) 26 { 27 Register(); 28 element = _Collection[serviceName] as WCFServiceElement; 29 if (null == element) 30 { 31 throw new NullReferenceException("可能配置出现严重错误,根据接口名称无法获取配置文件信息."); 32 } 33 } 34 35 switch (element.Binding) 36 { 37 case "BasicHttpBinding": 38 _Binding = new BasicHttpBinding(); 39 break; 40 case "WSHttpBinding": 41 _Binding = new WSHttpBinding(); 42 break; 43 case "WS2007HttpBinding": 44 _Binding = new WS2007HttpBinding(); 45 break; 46 default: 47 throw new ArgumentNullException("根据接口名称无法匹配绑定类型"); 48 } 49 50 return _Binding; 51 } 52 53 public static EndpointAddress CreateAddress(string serviceName) 54 { 55 WCFServiceElement element = _Collection[serviceName] as WCFServiceElement; 56 if (null == element) 57 { 58 Register(); 59 element = _Collection[serviceName] as WCFServiceElement; 60 if (null == element) 61 { 62 throw new NullReferenceException("可能配置出现严重错误,根据接口名称无法获取配置文件信息."); 63 } 64 } 65 66 EndpointAddress _Address = new EndpointAddress(element.IPAddress); 67 return _Address; 68 } 69 }
这里主要用来根据配置动态配置WCF Client 和 Binding 的。然后再您的客户端以及配置文件注册如下(我这里提供的是ASP.NET MVC 5 的环境):
1 public class MvcApplication : HttpApplication 2 { 3 protected void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); 6 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 7 RouteConfig.RegisterRoutes(RouteTable.Routes); 8 BundleConfig.RegisterBundles(BundleTable.Bundles); 9 10 WCFClientConfigurationManagement.Register(); 11 } 12 }
1 <configuration> 2 <configSections> 3 <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> 4 <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> 5 <section name="WCFServices" type="Basic.Configuration.WCFServiceSection,Basic.Data"/> 6 </configSections> 7 <WCFServices> 8 <WCFService InterfaceServiceName="IAppManagement" 9 IPAddress="http://192.168.1.4:8085/AppManagementService" 10 Binding="WSHttpBinding"> 11 </WCFService> 12 </WCFServices> 13 </configuration>
4.定义适合自己的WCF ClientBase
1 public class WCFClient 2 { 3 public static TResult ClientInvoke<TChannel , TResult>(Func<TChannel , TResult> functionInvoke) 4 { 5 string _ServiceName = typeof(TChannel).Name; 6 Binding binding = WCFClientConfigurationManagement.CreateBinding(_ServiceName); 7 EndpointAddress address = WCFClientConfigurationManagement.CreateAddress(_ServiceName); 8 TChannel _channel = ChannelFactory<TChannel>.CreateChannel(binding, address); 9 TResult _Result = functionInvoke.Invoke(_channel); 10 return _Result; 11 } 12 }
以上交代完毕,当然提醒初步了解WCF的人IPAddress 配置也可以写*.svc地址,可以在自寄宿在Windows Service 里,也可以是IIS宿主环境。写到这里,希望这篇随笔能帮助到需要帮助的人,这也就是它的价值所在了。