Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

WCF 服务的集合管理器的设计

可均可可 2019-02-01 15:37:00 阅读数:225 评论数:0 点赞数:0 收藏数:0

今天是2019年2月1日,时间过得针对,马上就年底了,当前新年也离我们越来越近了。在此,我也祝福经常浏览我博客的朋友们“新年快乐、阖家欢乐”,来年有一个好彩头。在即将结束这一年之计,写今年的最后一片文章。WCF 我相信大家都使用过,每次宿主该服务的时候都要使用 ServiceHost,如果要加载多个 WCF 服务,那就需要多次 ServiceHost 实例化,而且这个过程大致都是一样的,这就有点太麻烦了。正好现在有时间,也有项目的需要,我就写了一份 WCF 服务的集合管理器,可以加载多个 WCF 服务,也可以对 WCF 的服务进行开启或者关闭的操作,使用起来还是比较方便的。这个设计已经改过多次,现在这个版本是目前最合适、最稳定的版本。

说写就写,OO的三大基本原则,1、面向抽象编程,不要面向实现编程;2、多组合少继承;3、哪里有变化点就封装哪里。

这三大原则我们要死死的记在心里,融化进血液里,由此,我的做法是做接口的抽象设计,代码如下:

1、接口 IWcfServiceManager 的设计如下:1 ///

2 ///WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。3 /// 4 public interfaceIWcfServiceManager:IDisposable5 {6 /// 7 ///以指定的名称增加 WCF 服务实例,但是该服务并没有启动。8 /// 9 /// 表示 WCF 服务的名称。 10 /// 返回布尔值,true 表示增加 WCF 服务成功,false 表示增加 WCF 失败。 11 bool AddService(stringserviceName);12 13 /// 14 ///从容器对象中删除指定名称的 WCF 服务实例。15 /// 16 /// 表示 WCF 服务的名称。 17 /// 返回布尔值,true 表示删除 WCF 服务成功,false 表示删除 WCF 服务失败。 18 bool RemoveService(stringserviceName);19 20 /// 21 ///获取所有的 WCF 服务实例的集合。22 /// 23 /// 返回所有的 WCF 服务实例集合。 24 IEnumerableGetServices();25 26 /// 27 ///根据指定的名称获取 WCF 服务实例。28 /// 29 /// 表示 WCF 服务的名称。 30 /// 返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。 31 WcfService GetService(stringserviceName);32 33 /// 34 ///开启指定名称 WCF 服务实例,此时该服务可以为客户端提供服务了。35 /// 36 /// 表示 WCF 服务的名称。 37 /// 返回布尔值,true 表示成功开启 WCF 服务,false 表示开启式 WCF 服务失败。 38 bool Start(stringserviceName);39 40 /// 41 ///开启所有的 WCF 服务实例。42 /// 43 voidStartAll();44 45 /// 46 ///关闭指定名称的 WCF 服务实例,此时该服务就不能为客户端提供任何服务了。47 /// 48 /// 表示 WCF 服务的名称。 49 /// 返回布尔值,true 表示成功关闭 WCF 服务实例,false 表示关闭 WCF 服务实例失败。 50 bool Close(stringserviceName);51 52 /// 53 ///关闭所有的 WCF 服务实例,停止所有的服务。54 /// 55 voidCloseAll();56 57 /// 58 ///根据指定的名称来判断该 WCF 服务实例是否已经开启。59 /// 60 /// 表示 WCF 服务的名称。 61 /// 返回布尔值,true 表示该名称的 WCF 服务实例是已经开启的,false 表示该名称的 WCF 服务实例是未开启的。 62 bool IsStartup(stringserviceName);63 64 /// 65 ///获取 WCF 服务实例的个数66 /// 67 int Count { get; }68 }

这个接口的设计就不多说了,很简单,继续我们下一步。

 

2、实现接口类的设计,类名是:WcfServiceManager.cs

该类型都有详细的备注信息,不用我多说了。1 ///

2 ///WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。3 /// 4 public abstract classWcfServiceManager : IWcfServiceManager, IDisposable5 {6 /#region 私有字段 7 8 private ConcurrentDictionaryserviceHostGroup;9 private ConcurrentDictionaryserviceHostTemp;10 private string[] _assemblyNames;11 private bool _disposed;//是否回收完毕 12 private IList_assemblies;13 14 /#endregion 15 16 /#region 构造函数 17 18 /// 19 ///初始化 WcfServiceManager 类的实例20 /// 21 protectedWcfServiceManager()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(stringserviceName)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 = newServiceHost(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(stringserviceName)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, outhostInstance);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 = newWcfService();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 returnlist;104 }105 106 /// 107 ///根据指定的名称获取 WCF 服务实例。108 /// 109 /// 表示 WCF 服务的名称。 110 /// 返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。 111 public WcfService GetService(stringserviceName)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 = newWcfService();121 service.ServiceName =_serviceHostGroup[serviceName].Description.Name;122 service.State =_serviceHostGroup[serviceName].State;123 service.Description =_serviceHostGroup[serviceName].Description;124 returnservice;125 }126 returnservice;127 }128 129 /// 130 ///清空容器中所有 WCF 服务实例。131 /// 132 public voidClearAll()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(stringserviceName)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,outtempHost);164 if (tempHost != null)165 {166 if (tempHost.State ==CommunicationState.Opened)167 {168 tempHost.Close();169 }170 tempHost = null;171 }172 ServiceHost newhost = newServiceHost(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 voidStartAll()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 = newServiceHost(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 = newServiceHost(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 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,outtempHost);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(stringserviceName)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 voidCloseAll()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(stringserviceName)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 voidReload()305 {306 this.CloseAll();307 this.ClearAll();308 this.Initialize();309 this.StartAll();310 }311 312 /// 313 ///获取 WCF 服务实例的个数314 /// 315 public intCount316 {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 incurrentDomainDlls)340 {341 _assemblies.Add(item);342 }343 foreach (var item inspecifiedDlls)344 {345 _assemblies.Add(item);346 }347 348 Configuration config =ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);349 ServiceModelSectionGroup serviceModelGroup = config.GetSectionGroup("system.serviceModel") asServiceModelSectionGroup;350 if (serviceModelGroup != null)351 {352 foreach (ServiceElement service inserviceModelGroup.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 inassemblyNames)369 {370 var assembly =Assembly.Load(item);371 assemblies.Add(assembly);372 }373 }374 returnassemblies;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 returnassemblies;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 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 returncurrentAssembly.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 voidDispose()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(booldisposing)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 classDefaultWcfServiceManager:WcfServiceManager, IDisposable5 {6 /#region 构造函数 7 8 /// 9 ///初始化 DefaultWcfServiceManager 类型的实例10 /// 11 publicDefaultWcfServiceManager(){ }12 13 /#endregion 14 }

主要的类型就差不多了。在这个设计过程中,还会涉及到一个辅助类型 WcfService

 

4、辅助类型 WcfService 的设计编码。很简单,直接上代码。

1 ///

2 ///WCF 服务实例的类型的定义3 /// 4 public sealed classWcfService5 {6 /#region 私有字段 7 8 private stringserviceName;9 privateCommunicationState communicationState;10 privateServiceDescription serviceDescription;11 12 /#endregion 13 14 /#region 构造函数 15 16 /// 17 ///初始化 WcfService 类型的实例18 /// 19 publicWcfService()20 { }21 22 /#endregion 23 24 /#region 实例属性 25 26 /// 27 ///获取或者设置 WCF 服务实例的名称28 /// 29 public stringServiceName30 {31 get { returnserviceName; }32 set 33 {34 if ((!string.IsNullOrEmpty(value)) && (!string.IsNullOrWhiteSpace(value)))35 {36 serviceName =value;37 }38 }39 }40 41 /// 42 ///获取或者设置 WCF 的服务实例的运行状态43 /// 44 publicCommunicationState State45 {46 get { returncommunicationState; }47 set { communicationState =value; }48 }49 50 /// 51 ///获取或者设置 WCF 服务的描述信息52 /// 53 publicServiceDescription Description54 {55 get { returnserviceDescription; }56 set 57 {58 if (value != null)59 {60 _serviceDescription =value;61 }62 }63 }64 65 /#endregion 66 }

 

5、单元测试项目代码。

这是最后的代码了,有源码没有测试代码,似乎还少一点。测试代码如下:

1 classProgram2 {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 = newDefaultWcfServiceManager();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 inhosts.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 端的,今天只是贴出了服务器端的代码,如果有需要,在把客户端生成代理类的代码贴出来。年尾了,让不好的东西过去,让自己迎接新的明天,不忘初心,继续努力。

版权声明
本文为[可均可可]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/PatrickLiu/p/10346046.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领