几年前搞wcf时发现每次新增业务封装类都要写对应的终结点配置,比较麻烦,研究了下 就自己写了个自动生成配置的代码。
实现思路如下:
- 反射加载程序集
- 根据业务类特性识别程序集
- 构造终结点配置string
- 生成配置文件
我的wcf 业务封装类都继承与public abstract class Base ,所以加载特定的程序集后将继承自Base 的业务类识别出来然后逐一生成终结点配置信息即可。
用的控制台来承载wcf
public class Program
{
static void Main()
{
//获取错误日志记录配置
log4net.Config.XmlConfigurator.Configure();
ILog InfoLog = LogManager.GetLogger("DBInfoLog");
//从程序集中获取服务类型列表
var servicelist = Assembly.GetExecutingAssembly().GetTypes().ToList().Where(x => x.BaseType == typeof(Base)).ToList();
if (servicelist == null || servicelist.Count == 0)
Console.WriteLine("没有要发布的wcf服务类型!");
#region 生成配置的xml,发布时候需要注释掉
获取app.config的目录
#if RELEASE
try
{
string path = AppDomain.CurrentDomain.BaseDirectory;
var removeString = path.Substring(path.LastIndexOf("bin"));
path = path.Replace(removeString, "App.config");
//如果能找到路径说明是开发模式下
if (File.Exists(path) == true)
{
MakeXmlConfig config = new MakeXmlConfig(path);
config.binding = "webHttpBinding";
config.endpointKind = "webHttpEndpoint";
config.behaviorConfiguration = "webHttpBehaviour";
config.TypeList = servicelist;
config.GetXml();
}
}
catch (Exception ex)
{
Base.MakeErrorLog(ex);
return;
}
#endif
#endregion
//服务存放字典
Dictionary<string, WebServiceHost> serviceList = new Dictionary<string, WebServiceHost>();
//记录服务名称
string name;
//服务对象
WebServiceHost host;
foreach (var item in servicelist)
{
name = item.Name;
host = new WebServiceHost(item);
serviceList.Add(name, host);
}
//遍历服务对象并打开服务
try
{
foreach (var item in serviceList)
item.Value.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
log4net.ILog log = log4net.LogManager.GetLogger("ApplicationInfoLog");
log.Error(ex.Message, ex);
}
Console.Read();
}
其中
MakeXmlConfig 定义如下
<pre name="code" class="csharp">public class MakeXmlConfig
{
/// <summary>
/// 服务名称
/// </summary>
public string serviceName { get; set; }
/// <summary>
/// 基地址 id地址
/// </summary>
public string BaseAddress { get; set; }
/// <summary>
/// 终结点传输类型名称
/// </summary>
public string endpointKind { get; set; }
/// <summary>
/// 绑定类型名称
/// </summary>
public string binding { get; set; }
/// <summary>
/// 终结点地址
/// </summary>
public string address { get; set; }
/// <summary>
/// 契约名称
/// </summary>
public string contract { get; set; }
/// <summary>
///bindingConfiguration 属性
/// </summary>
public string bindingConfiguration { get; set; }
/// <summary>
/// behaviorConfiguration 属性
/// </summary>
public string behaviorConfiguration { get; set; }
/// <summary>
///服务类型列表
/// </summary>
public List<Type> TypeList { get; set; }
/// <summary>
/// 要插入的xml结果列表
/// </summary>
List<string> xmlList { get; set; }
/// <summary>
/// 已经存在的服务列表
/// </summary>
List<string> ExistsList { get; set; }
/// <summary>
/// xml文件全路径
/// </summary>
string filename { get; set; }
public MakeXmlConfig(string filename)
{
this.filename = filename;
this.TypeList = new List<Type>();
this.xmlList = new List<string>();
this.ExistsList = new List<string>();
this.BaseAddress = this.GetBaseAddress(this.filename);
}
/// <summary>
/// 根据具体的服务类型进行拼接生成xml
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
string CreateXml(Type type)
{
var basenterfaceName = type.BaseType.GetInterfaces()[0].FullName;
var sign="\"";
serviceName = sign + type.FullName + sign;
var bindingConfiguration = sign + this.bindingConfiguration + sign;
var kind = sign + endpointKind + sign;
var behaviorConfiguration = sign + this.behaviorConfiguration + sign;
address =sign + BaseAddress + "/" + type.Name.Replace("Service", "") + sign;
contract = sign + type.GetInterfaces().Where(x => x.FullName != basenterfaceName).ToList()[0].FullName + sign;
return @"<service name="+serviceName+ @">
<endpoint kind=" + kind + @" behaviorConfiguration="+behaviorConfiguration+@"
address=" +address+@"
contract=" + contract + @"/> "+Environment.NewLine+"</service>";
}
/// <summary>
/// 获得要产生xml的列表
/// </summary>
public void GetXml()
{
this.GetXmlServicesList(this.filename);
foreach (var item in TypeList)
{
if (this.ExistsList.Contains(item.FullName) == true)
continue;
var result = this.CreateXml(item);
this.xmlList.Add(result);
}
this.SaveXmlFile(this.filename);
}
/// <summary>
///获得已经存在的服务列表
/// </summary>
/// <param name="filename"></param>
private void GetXmlServicesList(string filename)
{
XmlDocument xmlDoc = new XmlDocument();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(filename, settings);
xmlDoc.Load(reader);
XmlNode xn = xmlDoc.SelectSingleNode("descendant::services");
if (xn == null)
return;
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode xnf in xnl)
{
XmlElement xe = (XmlElement)xnf;
var attribute = xe.GetAttribute("name");
this.ExistsList.Add(attribute);
}
reader.Close();
}
/// <summary>
/// 保存列表
/// </summary>
private void SaveXmlFile(string filename)
{
//如果没有要写入的就返回
if (this.xmlList == null || this.xmlList.Count == 0)
return;
StringBuilder result = new StringBuilder();
//拼接要保存的配置文件
foreach (var item in this.xmlList)
result.Append(item+Environment.NewLine);
//确认文件是否存在
if (File.Exists(filename) == false)
{
Console.WriteLine("要设置的配置文件:{0}不存在,请确认!",filename);
return;
}
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);
StreamReader sm = new StreamReader(fs);
//读取所有内容
string txt = sm.ReadToEnd();
sm.Close();
fs.Close();
//查找要插入的节点
int index = txt.LastIndexOf("</services>");
if (index == -1)
{
Console.WriteLine("请配置<services></services>节点");
return;
}
txt= txt.Insert(index, result.ToString());
System.IO.File.WriteAllText(filename, txt);
}
private string GetBaseAddress(string filename)
{
string baseAddress ="";
XmlDocument xmlDoc = new XmlDocument();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
XmlReader reader = XmlReader.Create(filename, settings);
xmlDoc.Load(reader);
XmlNode node = xmlDoc.SelectSingleNode("//system.serviceModel//serviceHostingEnvironment//serviceActivations");//
if (node != null && node.ChildNodes[0] != null)
{
baseAddress = node.ChildNodes[0].Attributes["relativeAddress"].Value;
}
else
{
Console.WriteLine("请配置基地址节点!");
}
reader.Close();
return baseAddress;
}
}
每次有新增业务类需要发布时,第一次运行控制台会自动重新生成一遍wcf 配置文件,然后再运行一遍即可测试,注意将wcf发布成windows 服务的时候参考如下代码
protected override void OnStart(string[] args)
{
#if DEBUG
Debugger.Launch(); //Launches and attaches a debugger to the process.
#endif
//从程序集中获取服务类型列表
//var servicelist = Assembly.GetExecutingAssembly().GetTypes().ToList().Where(x => x.BaseType == typeof(Base)).ToList();
var servicelist = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "//EPSBX.WcfServices.Service.exe")
.GetTypes().ToList().Where(x => x.BaseType ==typeof(Base)).ToList();
if (servicelist == null)
Console.WriteLine("没有要发布的wcf服务类型!");
//服务存放字典
Dictionary<string, WebServiceHost> serviceList = new Dictionary<string,WebServiceHost>();
//记录服务名称
string name;
//服务对象
WebServiceHost host;
foreach (var item in servicelist)
{
name = item.Name;
host = new WebServiceHost(item);
serviceList.Add(name, host);
}
//Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory +"//EPS.WcfServices.Service.Interface.dll");
//遍历服务对象并打开服务
try
{
foreach (var item in serviceList)
item.Value.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
其中有个调试技巧,还是比较有用的,毕竟window 服务不太好调试
#if DEBUG
Debugger.Launch(); //Launches and attaches a debugger to the process.
#endif
如果您觉得我的文章对您有所帮助,欢迎扫码进行赞赏!