辛酸與幸福的故事,讓我最近心如止水,越來越平靜,越來越執着於我們的未來,越來越自信於自己的事業。
從今天開始,我每天會整理自己的文檔,把自己的思路和架構思想拿出來分享,共同學習。
聲明下,沒有花工作的時間來寫這個,一般習慣於夜深人靜的時候,一邊聽着《財經夜讀》一邊總結着自己在軟件領域裏的知識。存檔,然後定期每天一發。
其實服務層架構,是我在兩年前開始研究,只是零零散散沒有弄個文檔出來,以下是我簡要的寫了下,算是關於服務架構第一篇的開題。希望有人給看法和建議,共同提高。
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來獲取最大值。當然實際只要你輸入非自然數,都可以是最大值了。