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

时间:2024-01-05 21:55:44

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

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

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

    

    1、接口 IWcfServiceManager 的设计如下:

     /// <summary>
/// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。
/// </summary>
public interface IWcfServiceManager:IDisposable
{
/// <summary>
/// 以指定的名称增加 WCF 服务实例,但是该服务并没有启动。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示增加 WCF 服务成功,false 表示增加 WCF 失败。</returns>
bool AddService(string serviceName); /// <summary>
/// 从容器对象中删除指定名称的 WCF 服务实例。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示删除 WCF 服务成功,false 表示删除 WCF 服务失败。</returns>
bool RemoveService(string serviceName); /// <summary>
/// 获取所有的 WCF 服务实例的集合。
/// </summary>
/// <returns>返回所有的 WCF 服务实例集合。</returns>
IEnumerable<WcfService> GetServices(); /// <summary>
/// 根据指定的名称获取 WCF 服务实例。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。</returns>
WcfService GetService(string serviceName); /// <summary>
/// 开启指定名称 WCF 服务实例,此时该服务可以为客户端提供服务了。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示成功开启 WCF 服务,false 表示开启式 WCF 服务失败。</returns>
bool Start(string serviceName); /// <summary>
/// 开启所有的 WCF 服务实例。
/// </summary>
void StartAll(); /// <summary>
/// 关闭指定名称的 WCF 服务实例,此时该服务就不能为客户端提供任何服务了。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示成功关闭 WCF 服务实例,false 表示关闭 WCF 服务实例失败。</returns>
bool Close(string serviceName); /// <summary>
/// 关闭所有的 WCF 服务实例,停止所有的服务。
/// </summary>
void CloseAll(); /// <summary>
/// 根据指定的名称来判断该 WCF 服务实例是否已经开启。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示该名称的 WCF 服务实例是已经开启的,false 表示该名称的 WCF 服务实例是未开启的。</returns>
bool IsStartup(string serviceName); /// <summary>
/// 获取 WCF 服务实例的个数
/// </summary>
int Count { get; }
}

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

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

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

     /// <summary>
/// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。
/// </summary>
public abstract class WcfServiceManager : IWcfServiceManager, IDisposable
{
#region 私有字段 private ConcurrentDictionary<string, ServiceHost> _serviceHostGroup;
private ConcurrentDictionary<string, ServiceHost> _serviceHostTemp;
private string[] _assemblyNames;
private bool _disposed;//是否回收完毕
private IList<Assembly> _assemblies; #endregion #region 构造函数 /// <summary>
/// 初始化 WcfServiceManager 类的实例
/// </summary>
protected WcfServiceManager()
{
_serviceHostGroup = new ConcurrentDictionary<string, ServiceHost>();
_serviceHostTemp = new ConcurrentDictionary<string, ServiceHost>();
_assemblies = new List<Assembly>();
} #endregion #region 接口方法的实现 /// <summary>
/// 以指定的名称增加 WCF 服务实例,但是该服务并没有启动。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示增加 WCF 服务成功,false 表示增加 WCF 失败。</returns>
public bool AddService(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
return false;
}
if (!_serviceHostGroup.ContainsKey(serviceName))
{
Type serviceType = GetServiceTypeFromAssemblies(serviceName,_assemblies);
if (serviceType != null)
{
ServiceHost host = new ServiceHost(serviceType);
_serviceHostGroup.TryAdd(serviceName, host);
return true;
}
else
{
return false;
}
}
return false;
} /// <summary>
/// 从容器对象中删除指定名称的 WCF 服务实例。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示删除 WCF 服务成功,false 表示删除 WCF 服务失败。</returns>
public bool RemoveService(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
return false;
}
if (_serviceHostGroup.ContainsKey(serviceName))
{
ServiceHost hostInstance = null;
_serviceHostGroup.TryRemove(serviceName, out hostInstance);
if (hostInstance != null && hostInstance.State == CommunicationState.Opened)
{
hostInstance.Close();
hostInstance = null;
}
return true;
}
return false;
} /// <summary>
/// 获取所有的 WCF 服务实例的集合。
/// </summary>
/// <returns>返回所有的 WCF 服务实例集合。</returns>
public IEnumerable<WcfService> GetServices()
{
IList<WcfService> list = new List<WcfService>();
if (_serviceHostGroup != null && _serviceHostGroup.Count > )
{
foreach (var key in _serviceHostGroup.Keys)
{
var service = new WcfService();
service.ServiceName = _serviceHostGroup[key].Description.Name;
service.State = _serviceHostGroup[key].State;
service.Description = _serviceHostGroup[key].Description;
list.Add(service);
}
}
return list;
} /// <summary>
/// 根据指定的名称获取 WCF 服务实例。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回和指定名称相匹配的 WCF 服务实例,如果不存在则会返回 Null 值。</returns>
public WcfService GetService(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
throw new ArgumentNullException("要查找的 WCF 服务的名称不能为空!");
}
WcfService service = null;
if (_serviceHostGroup.ContainsKey(serviceName))
{
service = new WcfService();
service.ServiceName = _serviceHostGroup[serviceName].Description.Name;
service.State = _serviceHostGroup[serviceName].State;
service.Description = _serviceHostGroup[serviceName].Description;
return service;
}
return service;
} /// <summary>
/// 清空容器中所有 WCF 服务实例。
/// </summary>
public void ClearAll()
{
if (_serviceHostGroup != null && _serviceHostGroup.Count > )
{
this.CloseAll();
_serviceHostGroup.Clear();
}
} /// <summary>
/// 开启指定名称 WCF 服务实例,此时该服务可以为客户端提供服务了。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示成功开启 WCF 服务,false 表示开启式 WCF 服务失败。</returns>
public bool Start(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
return false;
}
var serviceHost = _serviceHostGroup[serviceName];
if (serviceHost != null)
{
if (serviceHost.State == CommunicationState.Created && serviceHost.State != CommunicationState.Faulted)
{
serviceHost.Open();
return true;
}
else if (serviceHost.State == CommunicationState.Closed || serviceHost.State != CommunicationState.Faulted)
{
ServiceHost tempHost;
_serviceHostGroup.TryRemove(serviceName,out tempHost);
if (tempHost != null)
{
if (tempHost.State == CommunicationState.Opened)
{
tempHost.Close();
}
tempHost = null;
}
ServiceHost newhost = new ServiceHost(serviceHost.Description.ServiceType);
newhost.Open();
_serviceHostGroup.TryAdd(serviceName, newhost);
return true;
}
}
return false;
} /// <summary>
/// 开启所有的 WCF 服务实例。
/// </summary>
public void StartAll()
{
if (_serviceHostGroup != null && _serviceHostGroup.Count > )
{
foreach (ServiceHost host in _serviceHostGroup.Values)
{
if (host.State != CommunicationState.Opened)
{
if (host.State == CommunicationState.Closed)
{
ServiceHost newhost = new ServiceHost(host.Description.ServiceType);
newhost.Open();
_serviceHostTemp.TryAdd(host.Description.ConfigurationName, newhost);
}
else if (host.State == CommunicationState.Faulted)
{
ServiceHost newhost = new ServiceHost(host.Description.ServiceType);
newhost.Open();
_serviceHostTemp.TryAdd(host.Description.ConfigurationName, newhost);
}
else if (host.State == CommunicationState.Created)
{
host.Open();
}
}
}
}
if (_serviceHostTemp != null && _serviceHostTemp.Count > )
{
foreach (KeyValuePair<string, ServiceHost> item in _serviceHostTemp)
{
if (_serviceHostGroup.ContainsKey(item.Key))
{
if (_serviceHostGroup[item.Key].State == CommunicationState.Opened)
{
_serviceHostGroup[item.Key].Close();
}
ServiceHost tempHost;
_serviceHostGroup.TryRemove(item.Key,out tempHost);
if (tempHost.State != CommunicationState.Closed)
{
tempHost.Close();
tempHost = null;
}
if (item.Value.State == CommunicationState.Closed)
{
item.Value.Open();
}
_serviceHostGroup.TryAdd(item.Key, item.Value);
}
}
_serviceHostTemp.Clear();
}
} /// <summary>
/// 关闭指定名称的 WCF 服务实例,此时该服务就不能为客户端提供任何服务了。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示成功关闭 WCF 服务实例,false 表示关闭 WCF 服务实例失败。</returns>
public bool Close(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
return false;
}
var host = _serviceHostGroup[serviceName];
if (host != null)
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
return true;
}
return false;
} /// <summary>
/// 关闭所有的 WCF 服务实例,停止所有的服务。
/// </summary>
public void CloseAll()
{
if (_serviceHostGroup != null && _serviceHostGroup.Count > )
{
foreach (ServiceHost host in _serviceHostGroup.Values)
{
if (host.State == CommunicationState.Opened)
{
host.Close();
}
}
}
} /// <summary>
/// 根据指定的名称来判断该 WCF 服务实例是否已经开启。
/// </summary>
/// <param name="serviceName">表示 WCF 服务的名称。</param>
/// <returns>返回布尔值,true 表示该名称的 WCF 服务实例是已经开启的,false 表示该名称的 WCF 服务实例是未开启的。</returns>
public bool IsStartup(string serviceName)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
return false;
}
var host = _serviceHostGroup[serviceName];
if (host != null)
{
if (host.State == CommunicationState.Opened)
{
return true;
}
}
return false;
} /// <summary>
/// 重新加载所有的 WCF 服务实例,并将所有的 WCF 服务对象开启
/// </summary>
public void Reload()
{
this.CloseAll();
this.ClearAll();
this.Initialize();
this.StartAll();
} /// <summary>
/// 获取 WCF 服务实例的个数
/// </summary>
public int Count
{
get
{
return _serviceHostGroup.Count;
}
} #endregion #region 定义的抽象方法 /// <summary>
/// 加载所有的 WCF 服务实例对象
/// </summary>
/// <param name="assemblyFullNames">承载 WCF 服务的应用程序集的完全限定名数组</param>
public void Initialize(params string[] assemblyFullNames)
{
_assemblyNames = assemblyFullNames;
CloseAll();
ClearAll(); var currentDomainDlls = GetAssembliesFromCurrentDomain();
var specifiedDlls = GetAssembliesFromSpecifiedCondition(_assemblyNames);
foreach (var item in currentDomainDlls)
{
_assemblies.Add(item);
}
foreach (var item in specifiedDlls)
{
_assemblies.Add(item);
} Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);
ServiceModelSectionGroup serviceModelGroup = config.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup;
if (serviceModelGroup != null)
{
foreach (ServiceElement service in serviceModelGroup.Services.Services)
{
this.AddService(service.Name);
}
}
} /// <summary>
/// 根据指定的字符串类型的程序集名称列表获取强类型的程序集列表
/// </summary>
/// <returns>返回获取到的强类型的程序集列表</returns>
protected virtual IList<Assembly> GetAssembliesFromSpecifiedCondition(params string[] assemblyNames)
{
IList<Assembly> assemblies = new List<Assembly>();
if (assemblyNames != null && assemblyNames.Length > )
{
foreach (var item in assemblyNames)
{
var assembly = Assembly.Load(item);
assemblies.Add(assembly);
}
}
return assemblies;
} /// <summary>
/// 根据当前的应用程序域获取所有必需的程序集
/// </summary>
/// <returns>返回获取到当前应用程序域内的程序集列表</returns>
protected virtual IList<Assembly> GetAssembliesFromCurrentDomain()
{
IList<Assembly> 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();
return assemblies;
} /// <summary>
/// 根据 WCF 服务的名称在当前程序域中或者传入的程序集中查找该服务的 Type 类型的对象
/// </summary>
/// <param name="serviceName">要查找的 WCF 服务的名称</param>
/// <param name="assemblies">承载 WCF 服务的程序集列表</param>
/// <returns>返回WCF服务的Type类型的对象,如果没有找到相应的类型就会返回 Null 值。</returns>
private Type GetServiceTypeFromAssemblies(string serviceName, IList<Assembly> assemblies)
{
if (string.IsNullOrEmpty(serviceName) || string.IsNullOrWhiteSpace(serviceName))
{
throw new ArgumentNullException("要查找的 WCF 服务的名称");
} if (assemblies == null || assemblies.Count == )
{
throw new ArgumentNullException("待查找的程序集列表不能为空!");
} try
{
if (assemblies != null && assemblies.Count() > )
{
var currentAssembly = assemblies.FirstOrDefault(a => a.GetType(serviceName) != null);
if (currentAssembly != null)
{
return currentAssembly.GetType(serviceName);
}
}
}
catch (Exception)
{
throw;
}
return null;
} #endregion #region IDispoable模式 /// <summary>
/// 释放托管资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} /// <summary>
/// 析构函数释放资源
/// </summary>
~WcfServiceManager()
{
Dispose(false);
} /// <summary>
/// 释放所有的托管资源和非托管资源核心方法实现
/// </summary>
/// <param name="disposing">是否需要释放那些实现IDisposable接口的托管对象</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return; //如果已经被回收,就中断执行
}
if (disposing)
{
//TODO:回收托管资源,调用IDisposable的Dispose()方法就可以
this.CloseAll();
this.ClearAll();
_serviceHostGroup = null;
}
//TODO:释放非托管资源,设置对象为null
_disposed = true;
} #endregion
}

    3、真正实现的叶子结点类型设计,类型是:DefaultWcfServiceManager.cs

        该类型就是用户将要使用的类型。

        

     /// <summary>
/// WCF 服务的实例管理器,该类型可以实现对容器内部的 WCF 服务对象进行增加、删除、查询、开启和关闭的操作。
/// </summary>
public sealed class DefaultWcfServiceManager:WcfServiceManager, IDisposable
{
#region 构造函数 /// <summary>
/// 初始化 DefaultWcfServiceManager 类型的实例
/// </summary>
public DefaultWcfServiceManager(){ } #endregion
}

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

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

      

     /// <summary>
/// WCF 服务实例的类型的定义
/// </summary>
public sealed class WcfService
{
#region 私有字段 private string _serviceName;
private CommunicationState _communicationState;
private ServiceDescription _serviceDescription; #endregion #region 构造函数 /// <summary>
/// 初始化 WcfService 类型的实例
/// </summary>
public WcfService()
{ } #endregion #region 实例属性 /// <summary>
/// 获取或者设置 WCF 服务实例的名称
/// </summary>
public string ServiceName
{
get { return _serviceName; }
set
{
if ((!string.IsNullOrEmpty(value)) && (!string.IsNullOrWhiteSpace(value)))
{
_serviceName = value;
}
}
} /// <summary>
/// 获取或者设置 WCF 的服务实例的运行状态
/// </summary>
public CommunicationState State
{
get { return _communicationState; }
set { _communicationState = value; }
} /// <summary>
/// 获取或者设置 WCF 服务的描述信息
/// </summary>
public ServiceDescription Description
{
get { return _serviceDescription; }
set
{
if (value != null)
{
_serviceDescription = value;
}
}
} #endregion
}

    5、单元测试项目代码。

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

        

     class Program
{
static void Main(string[] args)
{
//DefaultWcfServiceManager hosts = new DefaultWcfServiceManager("ServiceInstance, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
DefaultWcfServiceManager hosts = new DefaultWcfServiceManager();
hosts.Initialize("ServiceInstance, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
//hosts.Initialize("ServiceInstance");
string operation = "a";
do
{
operation = Console.ReadLine();
if (string.Compare(operation, "StartAll", true) == )
{
hosts.StartAll();
Console.WriteLine("已经全部打开");
} if (string.Compare(operation, "ConsoleService", true) == )
{
hosts.Close("ServiceInstance.ConsoleService");
Console.WriteLine("ConsoleService 已经关闭");
} if (string.Compare(operation, "ConsoleServiceOpen", true) == )
{
hosts.Start("ServiceInstance.ConsoleService");
Console.WriteLine("ConsoleService 已经打开");
} if (string.Compare(operation, "MathServiceOpen", true) == )
{
hosts.Start("ServiceInstance.MathService");
Console.WriteLine("MathService 已经打开");
} if (string.Compare(operation, "MathService", true) == )
{
hosts.Close("ServiceInstance.MathService");
Console.WriteLine("MathService 已经关闭");
} if (string.Compare(operation, "CloseAll", true) == )
{
hosts.CloseAll();
Console.WriteLine("已经全部关闭");
} if (string.Compare(operation, "Reload", true) == )
{
hosts.Reload();
Console.WriteLine("已经全部重新打开");
}
if (string.Compare(operation, "print", true) == )
{
foreach (var item in hosts.GetServices())
{
Console.WriteLine("服务地址:" + item.Description.Endpoints[].Address.Uri.ToString() + ";状态:" + item.State.ToString());
}
}
} while (string.Compare(operation, "exit", true) != );
}
}

    总结:

      好了,就写到这里吧。要想使用 WCF ,必须的命名空间是必须要引入的 System.ServiceModel,当然这里省略了必要的配置数据了,我相信,这个不是很难。也要说明一点,我这个项目是放在类库里面的,WCF 是分为 Client 端和 Server 端的,今天只是贴出了服务器端的代码,如果有需要,在把客户端生成代理类的代码贴出来。年尾了,让不好的东西过去,让自己迎接新的明天,不忘初心,继续努力。