今天是2019年2月1日,时间过得针对,马上就年底了,当前新年也离我们越来越近了。在此,我也祝福经常浏览我博客的朋友们“新年快乐、阖家欢乐”,来年有一个好彩头。在即将结束这一年之计,写今年的最后一片文章。WCF 我相信大家都使用过,每次宿主该服务的时候都要使用 ServiceHost,如果要加载多个 WCF 服务,那就需要多次 ServiceHost 实例化,而且这个过程大致都是一样的,这就有点太麻烦了。正好现在有时间,也有项目的需要,我就写了一份 WCF 服务的集合管理器,可以加载多个 WCF 服务,也可以对 WCF 的服务进行开启或者关闭的操作,使用起来还是比较方便的。这个设计已经改过多次,现在这个版本是目前最合适、最稳定的版本。
说写就写,OO的三大基本原则,1、面向抽象编程,不要面向实现编程;2、多组合少继承;3、哪里有变化点就封装哪里。
这三大原则我们要死死的记在心里,融化进血液里,由此,我的做法是做接口的抽象设计,代码如下:
1、接口 IWcfServiceManager 的设计如下:
1 ///2 /// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。 3 /// 4 public interface IWcfServiceManager:IDisposable 5 { 6 /// 7 /// 以指定的名称增加 WCF 服务实例,但是该服务并没有启动。 8 /// 9 /// 表示 WCF 服务的名称。 10 /// 返回布尔值,true 表示增加 WCF 服务成功,false 表示增加 WCF 失败。 11 bool AddService(string serviceName); 12 13 /// 14 /// 从容器对象中删除指定名称的 WCF 服务实例。 15 /// 16 /// 表示 WCF 服务的名称。 17 /// 返回布尔值,true 表示删除 WCF 服务成功,false 表示删除 WCF 服务失败。 18 bool RemoveService(string serviceName); 19 20 /// 21 /// 获取所有的 WCF 服务实例的集合。 22 /// 23 /// 返回所有的 WCF 服务实例集合。 24 IEnumerable GetServices(); 25 26 /// 27 /// 根据指定的名称获取 WCF 服务实例。 28 /// 29 /// 表示 WCF 服务的名称。 30 /// 返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。 31 WcfService GetService(string serviceName); 32 33 /// 34 /// 开启指定名称 WCF 服务实例,此时该服务可以为客户端提供服务了。 35 /// 36 /// 表示 WCF 服务的名称。 37 /// 返回布尔值,true 表示成功开启 WCF 服务,false 表示开启式 WCF 服务失败。 38 bool Start(string serviceName); 39 40 /// 41 /// 开启所有的 WCF 服务实例。 42 /// 43 void StartAll(); 44 45 /// 46 /// 关闭指定名称的 WCF 服务实例,此时该服务就不能为客户端提供任何服务了。 47 /// 48 /// 表示 WCF 服务的名称。 49 /// 返回布尔值,true 表示成功关闭 WCF 服务实例,false 表示关闭 WCF 服务实例失败。 50 bool Close(string serviceName); 51 52 /// 53 /// 关闭所有的 WCF 服务实例,停止所有的服务。 54 /// 55 void CloseAll(); 56 57 /// 58 /// 根据指定的名称来判断该 WCF 服务实例是否已经开启。 59 /// 60 /// 表示 WCF 服务的名称。 61 /// 返回布尔值,true 表示该名称的 WCF 服务实例是已经开启的,false 表示该名称的 WCF 服务实例是未开启的。 62 bool IsStartup(string serviceName); 63 64 /// 65 /// 获取 WCF 服务实例的个数 66 /// 67 int Count { get; } 68 }
这个接口的设计就不多说了,很简单,继续我们下一步。
2、实现接口类的设计,类名是:WcfServiceManager.cs
该类型都有详细的备注信息,不用我多说了。
1 ///2 /// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。 3 /// 4 public abstract class WcfServiceManager : IWcfServiceManager, IDisposable 5 { 6 #region 私有字段 7 8 private ConcurrentDictionary<string, ServiceHost> _serviceHostGroup; 9 private ConcurrentDictionary<string, ServiceHost> _serviceHostTemp; 10 private string[] _assemblyNames; 11 private bool _disposed;//是否回收完毕 12 private IList _assemblies; 13 14 #endregion 15 16 #region 构造函数 17 18 /// 19 /// 初始化 WcfServiceManager 类的实例 20 /// 21 protected WcfServiceManager() 22 { 23 _serviceHostGroup = new ConcurrentDictionary<string, ServiceHost>(); 24 _serviceHostTemp = new ConcurrentDictionary<string, ServiceHost>(); 25 _assemblies = new List (); 26 } 27 28 #endregion 29 30 #region 接口方法的实现 31 32 /// 33 /// 以指定的名称增加 WCF 服务实例,但是该服务并没有启动。 34 /// 35 /// 表示 WCF 服务的名称。 36 /// 返回布尔值,true 表示增加 WCF 服务成功,false 表示增加 WCF 失败。 37 public bool AddService(string serviceName) 38 { 39 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 40 { 41 return false; 42 } 43 if (!_serviceHostGroup.ContainsKey(serviceName)) 44 { 45 Type serviceType = GetServiceTypeFromAssemblies(serviceName,_assemblies); 46 if (serviceType != null) 47 { 48 ServiceHost host = new ServiceHost(serviceType); 49 _serviceHostGroup.TryAdd(serviceName, host); 50 return true; 51 } 52 else 53 { 54 return false; 55 } 56 } 57 return false; 58 } 59 60 /// 61 /// 从容器对象中删除指定名称的 WCF 服务实例。 62 /// 63 /// 表示 WCF 服务的名称。 64 /// 返回布尔值,true 表示删除 WCF 服务成功,false 表示删除 WCF 服务失败。 65 public bool RemoveService(string serviceName) 66 { 67 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 68 { 69 return false; 70 } 71 if (_serviceHostGroup.ContainsKey(serviceName)) 72 { 73 ServiceHost hostInstance = null; 74 _serviceHostGroup.TryRemove(serviceName, out hostInstance); 75 if (hostInstance != null && hostInstance.State == CommunicationState.Opened) 76 { 77 hostInstance.Close(); 78 hostInstance = null; 79 } 80 return true; 81 } 82 return false; 83 } 84 85 /// 86 /// 获取所有的 WCF 服务实例的集合。 87 /// 88 /// 返回所有的 WCF 服务实例集合。 89 public IEnumerable GetServices() 90 { 91 IList list = new List (); 92 if (_serviceHostGroup != null && _serviceHostGroup.Count > 0) 93 { 94 foreach (var key in _serviceHostGroup.Keys) 95 { 96 var service = new WcfService(); 97 service.ServiceName = _serviceHostGroup[key].Description.Name; 98 service.State = _serviceHostGroup[key].State; 99 service.Description = _serviceHostGroup[key].Description; 100 list.Add(service); 101 } 102 } 103 return list; 104 } 105 106 /// 107 /// 根据指定的名称获取 WCF 服务实例。 108 /// 109 /// 表示 WCF 服务的名称。 110 /// 返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。 111 public WcfService GetService(string serviceName) 112 { 113 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 114 { 115 throw new ArgumentNullException("要查找的 WCF 服务的名称不能为空!"); 116 } 117 WcfService service = null; 118 if (_serviceHostGroup.ContainsKey(serviceName)) 119 { 120 service = new WcfService(); 121 service.ServiceName = _serviceHostGroup[serviceName].Description.Name; 122 service.State = _serviceHostGroup[serviceName].State; 123 service.Description = _serviceHostGroup[serviceName].Description; 124 return service; 125 } 126 return service; 127 } 128 129 /// 130 /// 清空容器中所有 WCF 服务实例。 131 /// 132 public void ClearAll() 133 { 134 if (_serviceHostGroup != null && _serviceHostGroup.Count > 0) 135 { 136 this.CloseAll(); 137 _serviceHostGroup.Clear(); 138 } 139 } 140 141 /// 142 /// 开启指定名称 WCF 服务实例,此时该服务可以为客户端提供服务了。 143 /// 144 /// 表示 WCF 服务的名称。 145 /// 返回布尔值,true 表示成功开启 WCF 服务,false 表示开启式 WCF 服务失败。 146 public bool Start(string serviceName) 147 { 148 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 149 { 150 return false; 151 } 152 var serviceHost = _serviceHostGroup[serviceName]; 153 if (serviceHost != null) 154 { 155 if (serviceHost.State == CommunicationState.Created && serviceHost.State != CommunicationState.Faulted) 156 { 157 serviceHost.Open(); 158 return true; 159 } 160 else if (serviceHost.State == CommunicationState.Closed || serviceHost.State != CommunicationState.Faulted) 161 { 162 ServiceHost tempHost; 163 _serviceHostGroup.TryRemove(serviceName,out tempHost); 164 if (tempHost != null) 165 { 166 if (tempHost.State == CommunicationState.Opened) 167 { 168 tempHost.Close(); 169 } 170 tempHost = null; 171 } 172 ServiceHost newhost = new ServiceHost(serviceHost.Description.ServiceType); 173 newhost.Open(); 174 _serviceHostGroup.TryAdd(serviceName, newhost); 175 return true; 176 } 177 } 178 return false; 179 } 180 181 /// 182 /// 开启所有的 WCF 服务实例。 183 /// 184 public void StartAll() 185 { 186 if (_serviceHostGroup != null && _serviceHostGroup.Count > 0) 187 { 188 foreach (ServiceHost host in _serviceHostGroup.Values) 189 { 190 if (host.State != CommunicationState.Opened) 191 { 192 if (host.State == CommunicationState.Closed) 193 { 194 ServiceHost newhost = new ServiceHost(host.Description.ServiceType); 195 newhost.Open(); 196 _serviceHostTemp.TryAdd(host.Description.ConfigurationName, newhost); 197 } 198 else if (host.State == CommunicationState.Faulted) 199 { 200 ServiceHost newhost = new ServiceHost(host.Description.ServiceType); 201 newhost.Open(); 202 _serviceHostTemp.TryAdd(host.Description.ConfigurationName, newhost); 203 } 204 else if (host.State == CommunicationState.Created) 205 { 206 host.Open(); 207 } 208 } 209 } 210 } 211 if (_serviceHostTemp != null && _serviceHostTemp.Count > 0) 212 { 213 foreach (KeyValuePair<string, ServiceHost> item in _serviceHostTemp) 214 { 215 if (_serviceHostGroup.ContainsKey(item.Key)) 216 { 217 if (_serviceHostGroup[item.Key].State == CommunicationState.Opened) 218 { 219 _serviceHostGroup[item.Key].Close(); 220 } 221 ServiceHost tempHost; 222 _serviceHostGroup.TryRemove(item.Key,out tempHost); 223 if (tempHost.State != CommunicationState.Closed) 224 { 225 tempHost.Close(); 226 tempHost = null; 227 } 228 if (item.Value.State == CommunicationState.Closed) 229 { 230 item.Value.Open(); 231 } 232 _serviceHostGroup.TryAdd(item.Key, item.Value); 233 } 234 } 235 _serviceHostTemp.Clear(); 236 } 237 } 238 239 /// 240 /// 关闭指定名称的 WCF 服务实例,此时该服务就不能为客户端提供任何服务了。 241 /// 242 /// 表示 WCF 服务的名称。 243 /// 返回布尔值,true 表示成功关闭 WCF 服务实例,false 表示关闭 WCF 服务实例失败。 244 public bool Close(string serviceName) 245 { 246 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 247 { 248 return false; 249 } 250 var host = _serviceHostGroup[serviceName]; 251 if (host != null) 252 { 253 if (host.State == CommunicationState.Opened) 254 { 255 host.Close(); 256 } 257 return true; 258 } 259 return false; 260 } 261 262 /// 263 /// 关闭所有的 WCF 服务实例,停止所有的服务。 264 /// 265 public void CloseAll() 266 { 267 if (_serviceHostGroup != null && _serviceHostGroup.Count > 0) 268 { 269 foreach (ServiceHost host in _serviceHostGroup.Values) 270 { 271 if (host.State == CommunicationState.Opened) 272 { 273 host.Close(); 274 } 275 } 276 } 277 } 278 279 /// 280 /// 根据指定的名称来判断该 WCF 服务实例是否已经开启。 281 /// 282 /// 表示 WCF 服务的名称。 283 /// 返回布尔值,true 表示该名称的 WCF 服务实例是已经开启的,false 表示该名称的 WCF 服务实例是未开启的。 284 public bool IsStartup(string serviceName) 285 { 286 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 287 { 288 return false; 289 } 290 var host = _serviceHostGroup[serviceName]; 291 if (host != null) 292 { 293 if (host.State == CommunicationState.Opened) 294 { 295 return true; 296 } 297 } 298 return false; 299 } 300 301 /// 302 /// 重新加载所有的 WCF 服务实例,并将所有的 WCF 服务对象开启 303 /// 304 public void Reload() 305 { 306 this.CloseAll(); 307 this.ClearAll(); 308 this.Initialize(); 309 this.StartAll(); 310 } 311 312 /// 313 /// 获取 WCF 服务实例的个数 314 /// 315 public int Count 316 { 317 get 318 { 319 return _serviceHostGroup.Count; 320 } 321 } 322 323 #endregion 324 325 #region 定义的抽象方法 326 327 /// 328 /// 加载所有的 WCF 服务实例对象 329 /// 330 /// 承载 WCF 服务的应用程序集的完全限定名数组 331 public void Initialize(params string[] assemblyFullNames) 332 { 333 _assemblyNames = assemblyFullNames; 334 CloseAll(); 335 ClearAll(); 336 337 var currentDomainDlls = GetAssembliesFromCurrentDomain(); 338 var specifiedDlls = GetAssembliesFromSpecifiedCondition(_assemblyNames); 339 foreach (var item in currentDomainDlls) 340 { 341 _assemblies.Add(item); 342 } 343 foreach (var item in specifiedDlls) 344 { 345 _assemblies.Add(item); 346 } 347 348 Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location); 349 ServiceModelSectionGroup serviceModelGroup = config.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup; 350 if (serviceModelGroup != null) 351 { 352 foreach (ServiceElement service in serviceModelGroup.Services.Services) 353 { 354 this.AddService(service.Name); 355 } 356 } 357 } 358 359 /// 360 /// 根据指定的字符串类型的程序集名称列表获取强类型的程序集列表 361 /// 362 /// 返回获取到的强类型的程序集列表 363 protected virtual IList GetAssembliesFromSpecifiedCondition(params string[] assemblyNames) 364 { 365 IList assemblies = new List (); 366 if (assemblyNames != null && assemblyNames.Length > 0) 367 { 368 foreach (var item in assemblyNames) 369 { 370 var assembly = Assembly.Load(item); 371 assemblies.Add(assembly); 372 } 373 } 374 return assemblies; 375 } 376 377 /// 378 /// 根据当前的应用程序域获取所有必需的程序集 379 /// 380 /// 返回获取到当前应用程序域内的程序集列表 381 protected virtual IList GetAssembliesFromCurrentDomain() 382 { 383 IList assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => (!a.FullName.StartsWith("System", StringComparison.OrdinalIgnoreCase) && (!a.FullName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase)) && (!a.FullName.StartsWith("mscorlib", StringComparison.OrdinalIgnoreCase)) && (!a.FullName.StartsWith("vshost32", StringComparison.OrdinalIgnoreCase)) && (!a.FullName.StartsWith("SMDiagnostics", StringComparison.OrdinalIgnoreCase)))).ToList(); 384 return assemblies; 385 } 386 387 /// 388 /// 根据 WCF 服务的名称在当前程序域中或者传入的程序集中查找该服务的 Type 类型的对象 389 /// 390 /// 要查找的 WCF 服务的名称 391 /// 承载 WCF 服务的程序集列表 392 /// 返回WCF服务的Type类型的对象,如果没有找到相应的类型就会返回 Null 值。 393 private Type GetServiceTypeFromAssemblies(string serviceName, IList assemblies) 394 { 395 if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName)) 396 { 397 throw new ArgumentNullException("要查找的 WCF 服务的名称"); 398 } 399 400 if (assemblies == null || assemblies.Count == 0) 401 { 402 throw new ArgumentNullException("待查找的程序集列表不能为空!"); 403 } 404 405 try 406 { 407 if (assemblies != null && assemblies.Count() > 0) 408 { 409 var currentAssembly = assemblies.FirstOrDefault(a => a.GetType(serviceName) != null); 410 if (currentAssembly != null) 411 { 412 return currentAssembly.GetType(serviceName); 413 } 414 } 415 } 416 catch (Exception) 417 { 418 throw; 419 } 420 return null; 421 } 422 423 #endregion 424 425 #region IDispoable模式 426 427 /// 428 /// 释放托管资源 429 /// 430 public void Dispose() 431 { 432 Dispose(true); 433 GC.SuppressFinalize(this); 434 } 435 436 /// 437 /// 析构函数释放资源 438 /// 439 ~WcfServiceManager() 440 { 441 Dispose(false); 442 } 443 444 /// 445 /// 释放所有的托管资源和非托管资源核心方法实现 446 /// 447 /// 是否需要释放那些实现IDisposable接口的托管对象 448 protected virtual void Dispose(bool disposing) 449 { 450 if (_disposed) 451 { 452 return; //如果已经被回收,就中断执行 453 } 454 if (disposing) 455 { 456 //TODO:回收托管资源,调用IDisposable的Dispose()方法就可以 457 this.CloseAll(); 458 this.ClearAll(); 459 _serviceHostGroup = null; 460 } 461 //TODO:释放非托管资源,设置对象为null 462 _disposed = true; 463 } 464 465 #endregion 466 }
3、真正实现的叶子结点类型设计,类型是:DefaultWcfServiceManager.cs
该类型就是用户将要使用的类型。
1 ///2 /// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。 3 /// 4 public sealed class DefaultWcfServiceManager:WcfServiceManager, IDisposable 5 { 6 #region 构造函数 7 8 /// 9 /// 初始化 DefaultWcfServiceManager 类型的实例 10 /// 11 public DefaultWcfServiceManager(){ } 12 13 #endregion 14 }
主要的类型就差不多了。在这个设计过程中,还会涉及到一个辅助类型 WcfService
4、辅助类型 WcfService 的设计编码。很简单,直接上代码。
1 ///2 /// WCF 服务实例的类型的定义 3 /// 4 public sealed class WcfService 5 { 6 #region 私有字段 7 8 private string _serviceName; 9 private CommunicationState _communicationState; 10 private ServiceDescription _serviceDescription; 11 12 #endregion 13 14 #region 构造函数 15 16 /// 17 /// 初始化 WcfService 类型的实例 18 /// 19 public WcfService() 20 { } 21 22 #endregion 23 24 #region 实例属性 25 26 /// 27 /// 获取或者设置 WCF 服务实例的名称 28 /// 29 public string ServiceName 30 { 31 get { return _serviceName; } 32 set 33 { 34 if ((!string.IsNullOrEmpty(value)) && (!string.IsNullOrWhiteSpace(value))) 35 { 36 _serviceName = value; 37 } 38 } 39 } 40 41 /// 42 /// 获取或者设置 WCF 的服务实例的运行状态 43 /// 44 public CommunicationState State 45 { 46 get { return _communicationState; } 47 set { _communicationState = value; } 48 } 49 50 /// 51 /// 获取或者设置 WCF 服务的描述信息 52 /// 53 public ServiceDescription Description 54 { 55 get { return _serviceDescription; } 56 set 57 { 58 if (value != null) 59 { 60 _serviceDescription = value; 61 } 62 } 63 } 64 65 #endregion 66 }
5、单元测试项目代码。
这是最后的代码了,有源码没有测试代码,似乎还少一点。测试代码如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //DefaultWcfServiceManager hosts = new DefaultWcfServiceManager("ServiceInstance, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); 6 DefaultWcfServiceManager hosts = new DefaultWcfServiceManager(); 7 hosts.Initialize("ServiceInstance, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); 8 //hosts.Initialize("ServiceInstance"); 9 string operation = "a"; 10 do 11 { 12 operation = Console.ReadLine(); 13 if (string.Compare(operation, "StartAll", true) == 0) 14 { 15 hosts.StartAll(); 16 Console.WriteLine("已经全部打开"); 17 } 18 19 if (string.Compare(operation, "ConsoleService", true) == 0) 20 { 21 hosts.Close("ServiceInstance.ConsoleService"); 22 Console.WriteLine("ConsoleService 已经关闭"); 23 } 24 25 if (string.Compare(operation, "ConsoleServiceOpen", true) == 0) 26 { 27 hosts.Start("ServiceInstance.ConsoleService"); 28 Console.WriteLine("ConsoleService 已经打开"); 29 } 30 31 if (string.Compare(operation, "MathServiceOpen", true) == 0) 32 { 33 hosts.Start("ServiceInstance.MathService"); 34 Console.WriteLine("MathService 已经打开"); 35 } 36 37 if (string.Compare(operation, "MathService", true) == 0) 38 { 39 hosts.Close("ServiceInstance.MathService"); 40 Console.WriteLine("MathService 已经关闭"); 41 } 42 43 if (string.Compare(operation, "CloseAll", true) == 0) 44 { 45 hosts.CloseAll(); 46 Console.WriteLine("已经全部关闭"); 47 } 48 49 if (string.Compare(operation, "Reload", true) == 0) 50 { 51 hosts.Reload(); 52 Console.WriteLine("已经全部重新打开"); 53 } 54 if (string.Compare(operation, "print", true) == 0) 55 { 56 foreach (var item in hosts.GetServices()) 57 { 58 Console.WriteLine("服务地址:" + item.Description.Endpoints[0].Address.Uri.ToString() + ";状态:" + item.State.ToString()); 59 } 60 } 61 } while (string.Compare(operation, "exit", true) != 0); 62 } 63 }
总结:
好了,就写到这里吧。要想使用 WCF ,必须的命名空间是必须要引入的 System.ServiceModel,当然这里省略了必要的配置数据了,我相信,这个不是很难。也要说明一点,我这个项目是放在类库里面的,WCF 是分为 Client 端和 Server 端的,今天只是贴出了服务器端的代码,如果有需要,在把客户端生成代理类的代码贴出来。年尾了,让不好的东西过去,让自己迎接新的明天,不忘初心,继续努力。