辛酸与幸福的故事,让我最近心如止水,越来越平静,越来越执着于我们的未来,越来越自信于自己的事业。
从今天开始,我每天会整理自己的文档,把自己的思路和架构思想拿出来分享,共同学习。
声明下,没有花工作的时间来写这个,一般习惯于夜深人静的时候,一边听着《财经夜读》一边总结着自己在软件领域里的知识。存档,然后定期每天一发。
其实服务层架构,是我在两年前开始研究,只是零零散散没有弄个文档出来,以下是我简要的写了下,算是关于服务架构第一篇的开题。希望有人给看法和建议,共同提高。
1 基于SOA架构思想设计
服务层有三个主要类:
Request:用户请求数据
Response:服务响应数据
以上两个类主要用于客户端的请求与响应操作。
BusinessHandler:业务处理程序
主要用于业务的父类,这是一个抽象类,用来约束业务遵循服务的规则。
2 服务规则
<Business>
<BusinessHandler type="CRM.Index;CRM" name="BH">
<Event name="LoadMenuTree">
<Request>
<Property type="String">UserId</Property>
<Property type="Menu">Menu</Property>
<Property type="TreeView">Tree</Property>
<Property type="String">Exit</Property>
</Request>
</Event>
<Event name="Login">
<Request>
<Property type="String">UserId</Property>
<Property type="String">UserPwd</Property>
<Property type="String">IP</Property>
</Request>
<Response>
<Property type="Boolean">IsLogin</Property>
<Property type="String">Theme</Property>
<Property type="String">UserName</Property>
</Response>
</Event>
</BusinessHandler>
</Business>
注意,在BusinessHandler节点里,属性type格式与持久数据层里的配置类似,用“;”隔开,前面为Class,后者为Assembly;而且Assembly有两种格式:
一是全名,这种可支持GAC:
CRM,Version=1.0.4.29079,Culture=neutral,PublicKeyToken=ba9122935f80dbe7
另一种是简写格式,一般不支持GAC,要求DLL文件在BIN或当前目录下:
CRM
最后,此业务规则,配有相应的样式表,供开发人员方便阅读规则。同时如果项目逐渐稳定,可以作为内嵌到DLL里,如Zivsoft.Services.Principle.dll。
3 服务层拓展
有时候,弱类型对于代码改动非常不利,所以会根据业务规则自动生成服务拓展层。在这里要感谢MS面试我的那几个面试官,是他们让我对这个拓展有了兴趣。
于是,便自定义出一个Tool自动XmlToCode,代码如下:(其实这个拓展层其实将会被废弃,因为有点得不偿失,时间久了,发现并不能弥补什么。)
PropertyReader.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
namespace Zivsoft.Services.XmlToCode
{
internal class Property
{
public string Type
{
get; set;
}
public string Name
{
get; set;
}
}
/// <summary>
///
/// </summary>
internal class PropertyReader
{
public void Execute()
{
var _ht=new Hashtable(0);
var al = GetPropertyList();
Property p;
var sb = new StringBuilder();
sb.AppendLine("namespace Zivsoft.Services.ServicesExt");
sb.AppendLine("{");
for (var i = 0; i < al.Count; i++)
{
p = al[i] as Property;
if (p == null) continue;
var type = p.Type.Replace("[]", "");
if(!_ht.Contains(type))//meet the first one
{
_ht.Add(type,new List<string> {p.Name});
}
else
{
var pro = _ht[type] as List<String>;
if (pro != null&&!pro.Contains(p.Name))pro.Add(p.Name);
}
}
foreach(string type in _ht.Keys)
{
sb.AppendLine("/tpublic enum E" + type);
sb.AppendLine("/t{");
var pro = _ht[type] as List<string>;
if (pro != null)
{
for (var i = 0; i < pro.Count; i++)
{
//property
sb.AppendLine("/t/t" + pro[i]+',');
}
}
sb.AppendLine("/t}");
}
sb.AppendLine("}");
var path = AppDomain.CurrentDomain.BaseDirectory + @"ServicesExt/";
if (!Directory.Exists(path))Directory.CreateDirectory(path);
var fs = File.Create(path+"Setting.cs");
var sw = new StreamWriter(fs);
sw.WriteLine(sb.ToString());
sw.Close();
fs.Close();
}
#region Private Methods
private static ArrayList GetPropertyList()
{
var al = new ArrayList();
var load = new XmlStreamReader();
var s = load.ReadStream("Service.xml");
var sr = new StreamReader(s);
while (!sr.EndOfStream)
{
var line = sr.ReadLine();
if (line.IndexOf("Property") == -1) continue;
var p = MatchLine(line);
if (p != null)
{
al.Add(p);
}
}
sr.Close();
return al;
}
private static Property MatchLine(string line)
{
line=line.Replace("<Property type=/"","").Replace("</Property>","").Replace("/t","");
var tag =new[]{"/">"};
var m=line.Split(tag,StringSplitOptions.RemoveEmptyEntries);
var type = m[0].Trim();
var name = m[1].Trim();
if (type == "" || name == "")
{
return null;
}
var p = new Property {Name = name, Type = type};
return p;
}
#endregion
}
}
SchemaExport.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Zivsoft.Services.XmlToCode
{
public class SchemaExport
{
public void Execute()
{
Outout("BH.cs", GetText("BH",ServiceHandler.BusinessHandlerList));
Outout("DM.cs", GetText("DM", ServiceHandler.DealMethodList));
var pReader = new PropertyReader();
pReader.Execute();
}
#region Private Method
/// <summary>
///
/// </summary>
/// <param name="csFileName"></param>
/// <param name="list"></param>
/// <returns></returns>
private static string GetText(string csFileName, List<string> list)
{
var sb = new StringBuilder();
sb.AppendLine("namespace Zivsoft.Services.ServicesExt");
sb.AppendLine("{");
sb.AppendLine(string.Format("/tpublic sealed class {0}", csFileName));
sb.AppendLine("/t{");
IEnumerator<string> en = list.GetEnumerator();
while (en.MoveNext())
{
sb.Append("/t/tpublic const string ");
sb.AppendLine(en.Current + "=/"" + en.Current + "/";");
}
sb.AppendLine("/t}");
sb.AppendLine("}");
return sb.ToString();
}
private static void Outout(string fileName, string content)
{
var path = AppDomain.CurrentDomain.BaseDirectory + @"ServicesExt/";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var fs = File.Open(path+fileName, FileMode.Create);
var sw = new StreamWriter(fs, Encoding.Unicode);
sw.WriteLine(content);
sw.Close();
fs.Close();
}
#endregion
}
}
4 日志跟踪
日志跟踪起源于开源框架log4net,配置在学习它的思想,但在我的框架里,我融入了自己的Color和Xml日志,同时也缩减了很多log4net里面的功能,这样才具它的易用性。
4.1 日志等级
所有日志分四个等级:Error > Warning > Info > Debug
以下是接口定义:
void LogError(string message, params object[] args)
void LogWarning(string message, params object[] args)
void LogInfo(string message, params object[] args)
void LogDebug(string message, params object[] args)
调用方式非常简单,举例如下:
Logger.LogError(“error”);
Logger.LogError(“failed with exception: {0}”, e);
4.2 日志文件配置
<Log level="DEBUG">
<LogFile enabled="false">log"log.log</LogFile>
<Console len="30">true</Console>
<XmlLog enabled="false">log"log.xml</XmlLog>
</Log>
说明:配置总体格式如上面所示,但有些参数需要在这里说明一下:
1) LogFile默认enabled为true,也就是说如果不指定enabled="false",文件日志不会关闭。
2) Console默认情况下也是为true的,而且长度len默认为Int32.MaxValue,在这里我们可以设置-1或者max来获取最大值。当然实际只要你输入非自然数,都可以是最大值了。