工作多年后才明白的.NET底層開發技術
參加工作已經有五年了。由于是非計算機科系,一開始的工作是做網頁,維護ASP腳本程序,有時候也要做圖片,總而言之,一項任務交到手里,沒有借口說不會做,想辦法彌補然后把任務做完。后來才得到機會,參與編程開發工作。而且,當時一心想用C#.NET做開發,不希望參與DELPHI的開發,偏偏工作經過層層的轉發,交到手里的就是DELPHI開發。經歷努力,再后來就真的用.NET進行開發工作,一直持續到現在。消極的觀念會說,學的很雜,做的也雜,積極一點的想法,把老板交給你的任務完成,就是稱職的員工。現在正值畢業生找工作的時間,一時找不到開發的工作,也不要急,先聽從公司的安排做個軟件測試,或是文檔管理之類的職位,等有空缺的開發人員的機會,你再毛遂自薦,同樣可以進入程序員這個領域。機會是留給有準備的人的,難道不是嗎?
一直用.NET做ERP/MIS類型的軟件,學業不精,可以完成工作任務,業余時間也制作一些集成化的工具包,下面分享一下我認為的.NET領域里的底層技術。
1 通信技術(Remoting,WCF, ASMX)
通常的三層架構,數據庫,數據訪問層,界面層。通訊技術定義了三層組件之間的調用約定,以及方法。這樣說起來有些抽象,舉例為證。現在要制作一個進銷存項目,基本的功能是進倉,出倉,轉倉,查詢功能要能查詢到倉庫進出日記帳,
庫存余額。對于一筆物料為FLEX0901的進倉業務,用ORM的方式完成,偽代碼如下
- InventoryMovmentEntity receipt=new InventoryMovmentEntity(“REF1108080001”,"RECEIPT”);
- InventoryMovmentDetailEntity detail=receipt.InventoryMovmentDetails.AddNew();
- detail.ItemNo=”FLEX0901”;
- detail.MoveDate=DateTime.Now;
- InventoryMovementDAL.Instance.Save(receipt);
這里有幾個問題處理的不錯,比如
1) ORM的數據讀寫,關注面從拼湊SQL讀寫到構建實體,這是進步。如果用DAL+SQL Script的方式完成,偽碼如下
- string mySql=”INSERT ICMOVH(RefNo,Direction) VALUES(‘REF1108080001’, 'RECEIPT’) ";
- InventoryMovementDAL.Instance.ExecuteNonQuery(mySql);
- mySql=”INSERT ICMOVD(ItemNo,MoveDate) VALUES(‘FLEX0901’,'2011/8/8’) ";
- InventoryMovementDAL.Instance.ExecuteNonQuery(mySql);
這里省略了拼湊參數值的句段,直接把值放到SQL語句中去。
后一種方式,明顯的缺陷時,當添加新的字段,對系統擴展時,修改和維護起來的成本,明顯大于前者。
2) 使用了單件模式,InventoryMovementDAL.Instance來統一操作數據讀寫,而不是這樣
- InventoryMovementDAL inventoryDAL=new InventoryMovementDAL();
- inventoryDAL.Save(receipt);
從這個單件模式,引出了通訊技術的必要性。當有很多個用戶,同時操作進倉功能,意味著同時有很多個InventoryMovementDAL的數據寫入操作,這時產生的問題
◆ 不好控制前后兩張單據的進倉單參考編號重復。為保證不重復,在保存之前,我們需要到數據庫檢查一次是否已經存在該參考編號的進倉單,也就是InventoryMovementDAL的Save方法的開頭,
要包含這樣一段代碼,以DAL+SQL Script的方式表示
- tring mySql=” SELECT COUNT(1) FROM ICMOVH WHERE RefNo=’REF1108080001’ ";
- bool existing=InventoryMovementDAL.Instance.ExecuteScalar(mySql)>0;
可以想像,當并發用戶為100時,每一筆進倉業務,需要預先一次數據檢查,來回于數據庫,這樣的程序性能肯定不好。
◆ 對于庫存報表,多個并發的InventoryMovementDAL會導致數據前后不統一。庫存余額報表現在可以讀到物料FLEX0901還有100個pc,一會當有進倉單入庫200pc的FLEX0901時,如果不手動刷新數據,此時的報表仍然顯示的物料FLEX0901的庫存余額是100,而不是300. 這有時候是不可接受的結果。
可以做一個timer,定期刷新庫存余額。這里的問題是,InventoryMovementDAL只有一個實例,無法自己告訴自己,已經有新的庫存了,重新讀取數據。這里需要一種并發機制,告訴InventoryMovementDAL實例,有新的數據加入到庫存余額中,需要刷新報表。
◆ 有一些數據項,是全局的,對于整個系統都是唯一的,需要特殊處理。
比如ERP系統允許的并發用戶,同時允許有10個用戶連接進入系統,超過則無法處理;
ERP系統的一個用戶把當前系統的默認貨幣從HK$改進了US$,其它的用戶,要能知道這個改變,此時,不太可能讓當前正在操作業務的用戶退出,重新進入系統;
當發生網絡故障時,ERP系統要能知道網絡故障,并suspend當前正在進行的操作;這一點可以理解為一條有效的改善,當無法連接到SQL Server時,要阻止當前正在錄入數據的用戶,否則他很辛苦的錄入了數據,而系統又無法保存,給出一個提示A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections 用戶會抱怨你的軟件沒有做好,盡管這不是你的錯。
如下圖,當前用戶正在編輯客戶資料,發生網絡故障,界面被Disable了。借助于通訊技術中的并發,可以實現。

通篇都沒有給Remoting,WCF打廣告,說Remoting,WCF的好處。當你的程序遇到這幾個問題時,是否可以考慮下通訊技術,來改善性能和客戶體驗。
2 反射,動態編譯
反射是動態獲取程序集的元數據的一種技術,這句話是做.NET程序員面試題目的一個的答案,你可選擇記住它,就好比高中生物學里面講到的細胞的結構的課程時,細胞由細胞膜,細胞質和細胞核組成。根據做程序的經驗,Never ask why不是好習慣,即使是微軟的API,有時候違反了調用約定,也會很抓狂。
請看下面這一段代碼
- Assembly assembly = Assembly.GetExecutingAssembly();
- object entryForm = Activator.CreateInstance(formBaseType) as Form;
- entryForm .MdiParent = this;
- entryForm .Show();
- entryForm .Activate();
代碼的含義比較簡單,從當前程序集中創建formBaseType類型,并調用它的方法。從方法名上來看,大概可以看出,這是一段MDI創建child子窗體,并顯示子窗體的代碼。
這段小代碼,也是插件式框架的基本思路,請參考《Management Console 工具管理類軟件通用開發框架(開放源碼)》中的例子來體會它的用處。
在ERP/MIS系統中,應用反射的例子,實在是太多了。數據訪問接口InventoryMovementDAL,借助于反射,來查找并調用它的實現類; 窗體也它的子窗體借助于反射來獲取屬性,傳遞值;ERP系統的整個框架,也是借助于反射搭建起來.
看下面的圖,ERP的三個模塊Paradox.ERP.SystemAdministration, Paradox.ERP.Engineering,Paradox.ERP.Inventory被Paradox.Framework.Kernal反射調用,如果再寫一個Paradox.ERP.Sales的銷售模塊,幾乎不需要改動,就可以讓它被框架調用。

動態編譯的例子,是應用到一個工資系統中. 請看圖

對于如何解析工資的formular公式,有若干種辦法,這里使有的是動態編譯的方法。把每一個工資項看成是一個類型class的屬性,Formular的內容則放到一個方法中去,當成表達式計算求職,最后應用反射,返回各屬性的值即可。
- public class FormularCalculation
- {
- public static object Build(string[] items, string formular)
- {
- string nameSpace = "A";
- string className = "FormularCalculation";
- string methodName = "Run";
- CSharpCodeProvider compiler = new CSharpCodeProvider();
- CompilerParameters paras = new CompilerParameters();
- paras.GenerateExecutable = false;
- paras.GenerateInMemory = true;
- StringBuilder classSrc = new StringBuilder();
- classSrc.Append(" using System;"+Environment.NewLine);
- classSrc.Append(" namespace "+nameSpace+" { " + Environment.NewLine);
- classSrc.Append("public class " + className + "{ " + Environment.NewLine);
- foreach (string item in items)
- {
- classSrc.Append("public decimal " + item + ";" + Environment.NewLine);
- }
- classSrc.Append("public void Run() { 基本工資=5000; " + Environment.NewLine);
- string [] format= Regex.Split(formular,Environment.NewLine);
- foreach (string prop in format)
- {
- classSrc.Append(prop +" ;"+ Environment.NewLine);
- }
- classSrc.Append("}"+Environment.NewLine);
- classSrc.Append("}" + Environment.NewLine);
- classSrc.Append("}" + Environment.NewLine);
- string source = classSrc.ToString();
- CompilerResults result = compiler.CompileAssemblyFromSource(paras, source);
- CompilerErrorCollection error= result.Errors;
- Assembly assembly = result.CompiledAssembly;
- object eval = assembly.CreateInstance(nameSpace+"."+className);
- MethodInfo method = eval.GetType().GetMethod(methodName);
- object reobj = method.Invoke(eval, null);
- return eval;
- }
- }
調用方法如下
string[] items = { "應發合計","基本工資","獎金","福利費", "扣款合計","社保","稅","實發合計","應稅所得額"};
string formular=@ ” 應發合計=基本工資+ 獎金 + 福利費 - 扣款合計;
扣款合計=社保 + 稅+ 應稅所得額;
實發合計=應發合計- 扣款合計; ";
- object obj = FormularCalculation.Build(items, formular);
- Type type = obj.GetType();
- foreach (PropertyInfo fi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public
- | BindingFlags.Instance | BindingFlags.DeclaredOnly))
- {
- string value = fi.Name; //取到計算后的各個屬性的值
- }
動態編譯還應用于軟件的加密,在內存中產生加密程序的源代碼,動態編譯并運行,檢測是否符合license授權。
3 ORM(NHibernate,LLBL Gen) 對象關系映射
雖然可以找出很多理由來拒絕ORM,比如performance不好,接口不好用,沒有NHibernate設計器,是的,這都是理由。但是,一旦接觸過ORM之后,我發現做系統再也離不開這個工具。相對于ERP/MIS類的系統,大部分時間都是和SQL的讀寫在搏斗,ORM帶給你的幾個好處,是不可忽視的。
1) 增加或刪除數據庫字段,界面和程序幾乎不需要改動。這一點我非常認同,即使是很穩定的系統,也避免不了要做customization,也要加些字段,如果用SQL拼湊,你幾乎要改動所有的相關內容,而且還無法獲取編譯器的語法檢查的好處,ORM在編譯期間就可以檢測出一些類型不匹配的問題。
2 )界面和邏輯的真正分離 改動計算邏輯,不需要改動界面,也就是實現MVC,MVP的模式,其實我們可以不用管這兩個模式,我們只是在用ORM來讀寫數據庫。
3 )代碼更幽雅,調試起來更容易,維護方便。
Linq技術之后,微軟大力發展Entity Framework,不推薦在項目中使用。MS的優點是,它發現一項技術很有用,或為開發一個很得力的工具,最終它會做的很好,比如Visual Studio,Office,但是這是需要時間的,它要不停的學習,觀察,改善,特別像API這類的東西,如果更新太快,會對項目產生較大的風險,實際項目中最需要的是穩定的API。MS打算要放棄的東西,它會慢慢減少資源,慢慢減少關注的次數,時間一長,最后就淡出了開發人員的視線。
NHibernate經過多年的發展,穩定,好用,有龐大的Java社區(Hibernate)的支持,不愁遇到問題沒有答案。
4 工作流 Workflow
目前微軟推出了兩個版本的工作流,.NET 3.5和.NET 4.0的,應該把它看成兩個產品,并不是簡單的版本升級。
ERP/MIS領域常見的需求有
1)采購單審批(如果條件) 要求:
當金額大于等于500且采購員為A時,需要通過May的審批
當金額大于等于500且采購員為B時,需要通過Jack的審批
當金額小于500時,不需要通過審批,可直接過帳
2 ) 當發生工程更改ECN時,要通知生產部重排計劃,通知貨倉安排發料。
像這種類型的需求,每個企業的要求都不一樣,要達到定制的目的,又不想為每個不同的客戶分別寫代碼,非用到工作流不可。你可以不選擇用工作流,那就為不同的客戶定制代碼,也行。
MS的工作流也做成了中間件,你需要盡可能的override

請通過查看《信息化基礎建設 工作流開發》,了解更多關于工作流的心得體會。
原文:http://www.cnblogs.com/JamesLi2015/archive/2011/08/09/2131729.html
【編輯推薦】