今天是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 IEnumerableGetServices();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_serviceHostGroup; 9 private ConcurrentDictionary _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(); 24 _serviceHostTemp = new ConcurrentDictionary (); 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 IEnumerableGetServices() 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 (KeyValuePairitem 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 Count316 {317 get318 {319 return _serviceHostGroup.Count;320 }321 }322 323 #endregion324 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 IListGetAssembliesFromSpecifiedCondition(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 IListGetAssembliesFromCurrentDomain()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, IListassemblies)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 try406 {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 #endregion424 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:释放非托管资源,设置对象为null462 _disposed = true;463 }464 465 #endregion466 }
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 #endregion14 }
主要的类型就差不多了。在这个设计过程中,还会涉及到一个辅助类型 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 #endregion13 14 #region 构造函数15 16 ///17 /// 初始化 WcfService 类型的实例18 /// 19 public WcfService()20 { }21 22 #endregion23 24 #region 实例属性25 26 ///27 /// 获取或者设置 WCF 服务实例的名称28 /// 29 public string ServiceName30 {31 get { return _serviceName; }32 set33 {34 if ((!string.IsNullOrEmpty(value)) && (!string.IsNullOrWhiteSpace(value)))35 {36 _serviceName = value;37 }38 }39 }40 41 ///42 /// 获取或者设置 WCF 的服务实例的运行状态43 /// 44 public CommunicationState State45 {46 get { return _communicationState; }47 set { _communicationState = value; }48 }49 50 ///51 /// 获取或者设置 WCF 服务的描述信息52 /// 53 public ServiceDescription Description54 {55 get { return _serviceDescription; }56 set57 {58 if (value != null)59 {60 _serviceDescription = value;61 }62 }63 }64 65 #endregion66 }
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 do11 {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 端的,今天只是贴出了服务器端的代码,如果有需要,在把客户端生成代理类的代码贴出来。年尾了,让不好的东西过去,让自己迎接新的明天,不忘初心,继续努力。