摘要:vb/c#.net實體代碼生成工具(entityscodegenerate)【ecg】是一款專門為.net資料庫程式開發量身定做的(orm架構)代碼生成工具,所生成的程式代碼基于oo、ado.net、分層架構、orm及反射+工廠設計模式等。支援.net1.1及以上版本,可用于oracle、sqlserver、sybase、db2、mysql、access、sqlite、postgresql、dm(達夢)、powerdesigner檔案、informix、firebird、maxdb、excel等和oledb、odbc連接配接的資料庫并可自定義,詳見文檔及安裝檔案的示例和工具的幫助文檔。
關鍵字: vb/c#.net實體代碼生成工具 實體代碼生成工具 entityscodegenerate
預期讀者: 軟體開發及相關人員
難度等級: 中
目前版本: 4.6
目 錄
長期以來,大多數項目或産品都使用關系型資料庫實作業務資料的存儲,這樣在開發過程中,常常有一些業務邏輯需要直接用寫sql語句實作,但這樣開發的結果是:遍地布滿sql語句。這些藕合性較高的sql語句給系統的改造和更新帶來很多無法預計的障礙。也許說可以使用服務端資料庫存儲子程式實作,這樣隻是将這種耦合搬遷到後端,問題依然沒有根本解決,服務端駐留過多的存儲子程式也消耗着伺服器的性能并給多人合作維護和更新部署帶來許多障礙。為了提高項目的靈活性,特别是快速開發,orm是一個不錯的選擇。舉個簡單的例子:在使用orm的系統中,當資料庫模型改變時,不再需要理會邏輯代碼和sql語句中涉及到該模型的所有改動,隻需要将該模型映射的對象稍作改動,甚至不做改動就可以滿足要求。
orm的全稱是objectrelational mapping,即對象關系映射。它的實質就是将關系資料(庫)中的業務資料用對象的形式表示出來,并通過面向對象(object-oriented)的方式将這些對象組織起來,實作系統業務邏輯的過程。在orm過程中最重要的概念是映射(mapping),通過這種映射可以使業務對象與資料庫分離。從面向對象來說,資料庫不應該和業務邏輯綁定到一起,orm則起到這樣的分離作用,使資料庫層透明,開發人員真正的面向對象。下圖簡單說明了orm在多層系統架構中的這個作用。
圖1 orm在多層系統架構中的作用
誠然orm并非是萬能的,面對紛繁複雜的業務邏輯,當遇到特别複雜的資料處理及海量資料運算或彌補設計的不足時還應歸結到sql或存儲過程來實作才是好的選擇,但它卻很好地展現了“80/20(或90/10)法則”(也被稱為“帕累托法則”),也就是說:花比較少(10%-20%)的力氣就可以解決大部分(80%-90%)的問題,這樣通過利用orm架構,我們就僅需要付出極少數時間和精力來解決剩下的少部分問題了,這無疑縮短了整個項目開發的周期,是以快速開發、面向對象和性能優化等必須靈活兼顧才好。orm産品應當預留适當的接口來做性能優化并對特定功能的補充支援,這樣才是一個好的産品,這些該工具都提供了很好的解決方案,下文分别作一簡單介紹。
好的orm工具不僅可以幫助我們很好的了解對象,而且工具本身會幫助我們記住字段屬性的業務含義并提供輔助應用。基于這個理念,一個基于.net的orm工具——vb/c#.net實體代碼生成工具(entityscodegenerate)便應運而生,該工具運作于.net2.0,适用性廣,開發後的代碼部署要求不高,在.net更高版本上也可以很好的運作。
該工具為orm提供了對象持久化查詢和事務處理等功能,包括對象的insert、update、delete、save、select等,對象查詢提供如getentity及構造函數擷取對象和實體集資訊等。工具職責是從資料庫中提取資料模型資訊并生成實體類,幫助開發人員快速映射到關系資料庫中的業務資料模型,最優化快速開發。目前提供直接從oracle、sqlserver、sybase、db2、access、mysql、sqlite、postgresql、dm(達夢)及oledb、custom(自定義)類型的資料庫和powerdesigner檔案中生成vb/c#.net代碼的支援,可以生成實體和實體集的相關代碼,并自動提取表及字段的注釋說明和對應的資料類型等資訊。
另外所生成的代碼檔案隻需修改資料庫連接配接,即可用于目前市場上支援ado.net的各種類型的資料庫,如oracle、sqlserver、sybase、db2、mysql、access、sqlite、postgresql、dm(達夢)、excel、informix、firebird、maxdb和oledb、odbc連接配接的資料庫等。所生成代碼檔案的類關系圖如下所示:
工具3.x版本之後的實體層代碼有個基類(baseentity),基類裡其實也正是你項目的資料庫連接配接的配置位置,該工具實際開發時的配置其實也就在這裡了,預設為生成代碼時所填寫的資料庫連接配接資訊(可手工擴充修改,如從緩存或config配置檔案中讀取、實作對資料庫連接配接資訊的加密/解密等;大多數時候我們隻需在getconnectionstring修改資料連接配接字元串即可):
public classbaseentity
{
public
static string getconnectionstring()
{
return
"user id=scott;password=tiger;data source= oracle9";//資料庫連接配接可修改從别處讀取
}
static databasetype getdatabasetype()
databasetype.oracle; //資料庫連接配接類型設定也可修改從别處讀取
……
}
這裡設定的資料庫連接配接類型及連接配接字元串是針對該命名空間下的所有實體(最初版本是放在全局設定dbconnectstring這個類裡面,缺陷是當一個項目有多個資料庫時将不好處理;而現在通過使用基類繼承及命名空間劃分則很容易解決)。
system.database.dbcore結合實體類可将簡單和複雜的資料庫操作及事務處理更為友善的實作,下文着重介紹在實際中的應用。
工具安裝後,在生成對應的實體代碼後有個“相關配置”檔案夾,裡面有“配置說明”文檔和相關檔案,實際使用時隻須按“配置說明”文檔操作即可。使用該工具開發的項目,可以做到很好的切換到異構資料庫,且隻需變動資料庫連接配接接口即可(即getdatabasetype()/getconnectionstring())。同時提供了ormaping.dll(1.0版本引用),system.database.dll(2.0/3.x/4.x版本引用)元件;安裝目錄下有相應的chm格式幫助文檔,是對這兩個dll單獨使用的說明,支援各種類型的資料庫通路,并可結合生成的實體操作資料庫,提供良好的事務處理等。其中ormaping.dll支援oracle;system.database.dll預設支援oracle資料庫,并可用于sqlserver、sybase、db2、mysql、sqlite、postgresql、informix、firebird、maxdb、dm(達夢)、oledb、odbc等。對沒有直接提供的資料庫可使用oledb或odbc連接配接。工具所生成的代碼和提供的檔案,都是可選配的,可單獨使用也可配置使用。實體代碼的生成界面比較簡單,如下所示:
這裡,隻須選擇資料庫類型、輸入正确的資料庫連接配接字元串(custom(自定義)的資料庫類型填寫正确的自定義程式集資訊)、代碼檔案的輸出目錄和代碼命名空間即可。實體資料類型以“資料類型映射檔案”為準,工具提供了系統預設的類型映射,若有自定義的資料類型,在“資料類型映射檔案”裡修改,可配置到資料類型的精确刻度。資料類型初始值在“資料類型初始值檔案”裡修改,沒有配置的取.net預設值。若修改過程中想恢複預設的配置,隻須點選右端“生成”按鈕即可(注意不同語言的類型定義可能有所差異,注意區分。如:c#的byte[],在vb中的定義為byte())。準備工作做好後,單擊“生成代碼”按鈕即可(若不想全部生成,單擊“選擇生成”按鈕)。生成成功之後按生成配置說明文檔的提示,将實體代碼檔案和基類檔案拷貝到指定目錄,并按生成代碼檔案所在“相關配置”目錄下的配置說明文檔,添加對應dll檔案的引用即可。操作簡單這裡就不在此贅述,下面以c#.net為例介紹實體對象的資料庫操作的工作,因篇幅所限,vb.net同理對比使用。(注:若想在.net1.1下使用,請設定隻生成實體(前字尾不填),并結合"示例代碼"目錄下"vs2003.rar"中的lxct.dbcore.dll使用)
先介紹單個實體類的資料庫操作。
2.2.1 單個實體對象的資料庫操作
這裡以oracle附帶庫dept為例來做說明,首先做對象的聲明
dept entity =new
dept();
此處以該對象來做闡述。
、擷取一個實體對象資訊
實體的操作預設以主鍵為準,對單個實體資訊的擷取可簡單如下實作:
entity.deptno
= 50;
entity = entity.getentity();
傳回主鍵字段deptno=50的對象,若資料庫中沒有對應的記錄則傳回null。dept表的主鍵字段是deptno,同時對聯合主鍵也是支援的,下文同此;getentity同時提供其它指定條件的重載,也可以使用getentitybyentitycondition,當指定條件有多個記錄符合時隻傳回首條記錄;傳回多條記錄可使用getdatatable方法。
在3.3版本之後也可通過多個構造函數來擷取單個實體對象的資訊等:
dept(50); //通過主鍵字段條件擷取實體對象
dept("deptno",50); //指定唯一字段條件擷取實體對象
dept(newstring[] {
"deptno" },new
object[] { 50 });
、插入一個實體對象資訊
插入一個對象代碼可如下所示:
= 51;
entity.dname
= "dname1";
entity.loc
= "loc1";
entity.insert();
同時insert提供多種相應的重載和insertall方法;差別是:insert在插入記錄時會比較對象初始的字段值,将與初始值不同的值插入,其餘的以表字段的預設方式處理;insertall則是插入全部字段,即使是對象的初始值沒有改變也會插入。
、更新一個實體對象資訊
更新一個對象代碼可如下所示:
entity.update();
update也提供多種相應的重載和updateall方法;差別是:update在更新記錄時會比較對象初始的字段值,将與初始值不同的值進行更新,其餘的表字段不更新;updateall則是更新全部字段,即使是對象的初始值沒有改變也會将對象的初始值更新到表裡。
、儲存一個實體對象資訊
儲存是該工具新增的功能,即将插入和更新合并為一個儲存方法,實體對象會自動根據主鍵限制解析是插入還是更新,代碼如下所示:
entity.save();
這裡是以主鍵為準,對多個聯合主鍵也是支援的。儲存是按主鍵判斷的(沒有主鍵的須指定限制字段),有就更新,沒有就插入新記錄。同時save提供多種重載和saveall、savebyentitycondition方法,save和saveall差別同插入和更新。
、删除一個實體對象資訊
删除可如下代碼所示:
entity.delete();
删除操作預設以主鍵為準,支援聯合主鍵,同時也提供多種指定條件的重載方法。最後附加說明,實體對象的增删改儲存操作都會傳回一個int值,該值傳回表中記錄受影響的行數。
從這些代碼可以明顯的看到,這裡常用的增、删、改、查操作隻需簡單幾句即可實作,少寫了很多代碼!
、取得實體映射表數值字段的最大值+1
代碼可如下:
int intid = entity.getint32maxid();
這裡擷取實體對象對應表字段預設第一個主鍵的最大值id+1,類型為整型,同時提供getint?maxid多種重載,即有整型和長整型及指定字段等重載。
除此之外,還提供了一系列的crud擴充方法,如:insertex / updateex / saveex / delinsert / delinsertex 和 實體集對象的批量操作,如:entitys.todatatable / save /saveall/saveex/delete/delinsert/delinsertex/getmaxvalue/getminvalue/getavgvalue/getcount/getsqlvalue/getsqldataset等;詳見示例代碼和生成的實體代碼及相關幫助文檔。其中實體集對象的批量操作自動啟用事務處理,操作成功統一送出,失敗時統一復原。
本節介紹的都是單實體/表無事務的操作,下節介紹多實體/表及事務處理的操作。
2.2.2 多個實體對象的資料庫操作
、實體集對象的使用
實體集除了提供基本的add和索引通路方法,同時也提供了相應與資料庫操作的方法,如:save/saveall/saveex/delete/delinsert/delinsertex/delinsertall/toxml/toxml_/fromxml/fromxmlfiletodatatable和實體集對象查詢。實體集對象與資料庫的操作釋義同單個實體,并且會自動啟動事務。實體集對象查詢是通過實體集構造函數擷取多實體對象資訊,如:
depts entity =new
depts(true); //擷取所有部門資訊
emps entitys
= new emps ("deptno",50) //擷取deptno=50所有雇員資訊
emps entitys1 =new
emps(newstring[] {
datatable dtbl = entitys.todatatable();//将擷取的實體集對象資訊轉換到資料表datatable
、結合事務處理
這裡簡略介紹實體對象結合system.database.dbcore的事務處理是如何工作的,先看以下代碼(可參見安裝示例代碼system.database.demo):
entitys.common.lc_worktype entity=
new entitys.common.lc_worktype();
entity.id
= 1;
entity.typename
= "typename";
string strconnection
= "password=cc;user id=cc;data source=oracle9";
dbcore dbcore
= new dbcore(databasetype.oracle, strconnection);
dbcore.open();
dbcore.begintransaction();
dbcore.save(new entitys.common.lc_worktype(), entity);
entity.description
= "類型描述";
= "作業類型";
dataset dst
= dbcore.executedataset("select * from lc_worktype");
datatable dt
= dbcore.getdatatablebyentitykey(entity);
int intrecord
= dbcore.delete(entity);
dt = dbcore.getdatatablebyentitykey(entity);
dbcore.committransaction();
dbcore.close();
這裡使用另外一個實體lc_worktype(映射到xx系統的"作業類型"這張表),begintransaction()為開始事務的标志;committransaction()為送出目前事務;還一個是rollbacktransaction()表示復原目前事務,放棄目前所有資料的更改。這樣在事務開始和送出或復原之間可以進行多個實體的操作,并将結果最終一起送出或復原撤銷。這裡save有兩個參數第一個是實體對象的初始類用于比較實體的初始值,第二個是要儲存的對象,該方法依據主鍵自動判斷是更新還是插入;與save類似的方法有saveall,同時也有重載及saveex、insert、insertall、insertex、update、updaall、update
ex、delete、isexitbyentitykey、exists、get?maxid等方法,可互相結合使用,方法都有詳盡的說明及示例代碼。執行中可單步跟蹤,檢視該事務下每步指令執行後對應的資料集資訊。
下面再看以oracle自帶的scott庫為例一段代碼delete、insert、update并結合事務使用的代碼:
= null;
try
emp entity1
= new emp();
dataset dst
= new dataset();
entity1.empno
= 7369; //設定主鍵empno為
entity1 = entity1.getentity(); //取得主鍵empno為實體對象資訊
//"user id=scott;password=tiger;data source=oracle9";
dbcore =
new dbcore(entitys.common.baseentity.getconnectionstring());
dbcore.open();
dbcore.begintransaction();
//選擇目前事務下的所有雇員emp的資訊
dst = dbcore.selectall().from(entity1).executedataset();
dbcore.delete(entity1);//删除主鍵empno為7369的記錄
dst = dbcore.selectall().from(entity1).executedataset();//檢視目前事務下記錄,目前删除記錄将不在此顯示
dbcore.insert(newemp(), entity1);//插入剛才删除主鍵empno為7369的記錄=dbcore.save(new
emp(), entity1);
dst = dbcore.selectall().from(entity1).executedataset();//檢視目前事務下記錄,可見剛剛插入的新記錄
entity1.sal
= entity1.sal + 100;//薪水加100
dbcore.update(newemp(), entity1);//更新=dbcore.save(new emp(), entity1);
dst = dbcore.selectall().from(entity1).executedataset();//檢視目前事務下記錄,對應薪水sal已更新
= entity1.sal - 100;//薪水減100
dbcore.committransaction();
dbcore.close();
catch (exception ex)
if (dbcore
!= null)
{
if (dbcore.istransaction)
dbcore.rollbacktransaction();//如果已經開始事務,則復原事務
dbcore.close();
}
上面的insert、update方法都可以save方法來取代,save方法會自動判斷是update還是insert,這裡隻是用來展示之用。
2.2.3 資料查詢(eql)及通用dml操作
如一般的關系資料庫所具有的查詢功能一樣,entityscodegenerate也有着非常豐富的查詢功能,如對象查詢、資料集查詢、函數查詢、條件查詢、排序查詢、分組等。工具的orm結構化實體查詢(eql)提供可以像寫sql語句一樣直接查詢。ecg提供entitycolumn類(或entity.s_靜态屬性)幫助system.database.dbcore.select從資料源查詢資料,select通過entitycolumn實體屬性(或entity.s_靜态屬性,下同),構造查詢并傳回結果資料集。(eql即entityquery
language)
常用實體對象查詢,見上節所述,這裡就不在贅述。
結構化查詢
這裡先介紹表實體原描述資訊類entitycolumn,這裡“entity”為表實體類對應的占位符,其類下有實體所對應的表的表名和表字段的公共靜态隻讀資訊。其中的tablename為每個該類的對應表名,各屬性字段為表名、字段名、字段對應類型的完全限定名,以鍵盤不能直接輸入的偏僻符号’┋’為分割符拼接而成。orm結構化查詢時可使用該類的屬性(或entity.s_靜态屬性,下同)。
、select查詢
system.database.dbcore類提供select方法及其相應的重載,以sqlserver自帶的pubs庫為例,可見如下代碼:
dataset dst =new
dataset();
dbcore dbcore =
new dbcore(databasetype.sqlserver,"server=(local);user id=sa;pwd=sa;database=pubs");
dataset dst =
dbcore.selectall().from("sales").executedataset();
dbcore.selectall().from(salescolumn.tablename).executedataset();
dbcore.select(salescolumn.ord_num).from(salescolumn.tablename).executedataset();
dbcore.select(salescolumn.ord_num).selectcolumn(salescolumn.ord_date).from(salescolumn.tablename).executedataset();
dbcore.selectall().from(newsales()).executedataset();
其中的dbcore.selectall()方法預設預設參數為查詢所有字段,select()不選擇出任何字段,僅初始化執行個體,同時也可直接使用sql文法的字元串或使用相應的entitycolumn類如salescolumn的屬性字段作為參數,或數組集合資訊。也可用selectcolumn添加要查詢的字段,selectcolumn(…)接受sql表字段名字元串或entitycolumn類的屬性字段格式的參數,selectcolumns(…)接受符合selectcolumn格式的數組參數,同時也提供selectcustom(…)接受自定義的sql文法的字元參數。同時鑒于selectcolumn等方法名較長,提供add替代selectcolumn,addmax、min、avg分别替代selectcolumnmax、min、avgvalue各種同構方法,便于代碼的抒寫。
from(…)為查詢的目标表名,這裡使用的是salescolumn.tablename,也可直接使用表名字元串,同時接受實體對象作為參數。executedataset()執行查詢并傳回在記憶體當中的dataset格式的結果資料集。executereader()執行查詢并傳回隻讀向前的資料結果集流idatareader;executescalar()快速執行查詢并傳回第一行第一列的object值,下同。
、from連接配接
先見如下代碼:
dataset();dataset dst1=
new dataset();
dst = dbcore.selectall().from().joininner("sales","stor_id ",
"stores","stor_id")
.executedataset();
dst = dbcore.selectall().from().joininner(salescolumn.stor_id,storescolumn.stor_id)
dst = dbcore.selectall().from().joinleft("sales","stor_id ",
dst = dbcore.selectall().from().joinleft(salescolumn.stor_id,storescolumn.stor_id)
dst = dbcore.selectall().from().joinright("sales","stor_id ",
dst = dbcore.selectall().from().joinright(salescolumn.stor_id,storescolumn.stor_id)
.executedataset()
dst = dbcore.selectall().from().joinfull("sales","stor_id ",
dst = dbcore.selectall().from().joinfull(salescolumn.stor_id,storescolumn.stor_id)
這裡是以sqlserver系統自帶pubs示例庫的表sales、stores為例介紹的,joininner為内連接配接、joinleft為左外連接配接、joinright為右外連接配接、joinfull完全外連接配接。參數可直接使用表名及表字段名字元串,或使用salescolumn、 storescolumn類,其中使用實體類靜态屬性字段代碼可讀性及維護性更好些。下面看下where的使用。
、where語句的condition條件
dataset dst1 =
.where().conditionandequal("sales","stor_id", 7067).executedataset();
dst1 = dbcore.selectall().from().joininner(salescolumn.stor_id,storescolumn.stor_id)
.where().conditionandequal(salescolumn.stor_id, 7067).executedataset();
.where().conditionandgreat("sales","stor_id", 7896)
. conditionorlessequal("sales","stor_id", 7067).executedataset();
.where().conditionandgreat(salescolumn.stor_id, 7896)
.conditionorlessequal(salescolumn.stor_id, 7067).executedataset();
dst = dbcore.selectall().from("sales").fromtable("stores")
.where().conditioncolumnandequal("sales","stor_id",
"stores","stor_id").
conditionandbetweenand("sales",
"stor_id", 7067, 7896).executedataset();
dst1 = dbcore.selectall().from(salescolumn.tablename).fromtable(storescolumn.tablename)
.where().conditioncolumnandequal(salescolumn.stor_id,storescolumn.stor_id).
conditionandbetweenand(salescolumn.stor_id, 7067, 7896).executedataset();
這裡再次展示了使用字元串和entitycolumn類的互相比較,可見使用entitycolumn更直覺,可讀及維護性更好些。where()是不帶參數的執行個體化方法,所添加的查詢條件均以condition…開頭,在這裡可以添加關系運算包括equal(=), less(<),great(>), lessequal(<=), greatequal (>=), notequal(<>、!=), betweenand, in, not in, null, not null,like,not like和各自的關系and,or比較
及表字段與字段之間的=,<, >, <= ,>=,<>關系and,or的連接配接等;另外還可以添加自定義的where條件語句等。
、order by排序功能
這裡切換到oracle資料庫,以oracle自帶的scott使用者為例,先看如下代碼:
emp entity =
new emp();
dbcore dbcore =new
dbcore(databasetype.oracle,"password=tiger;user id=scott;data source=oracle9");
dst = dbcore.selectall().from(entity)
.where().conditionandgreat(entity,empcolumn.deptno, 20)
.orderby(empcolumn.sal).executedataset();
.orderby().asc(empcolumn.sal).executedataset();
.where().conditionandgreat(entity, empcolumn.deptno, 20)
.orderby().desc(empcolumn.sal).executedataset();
dst = dbcore.selectall().from(empcolumn.tablename)
.where().conditionandgreat(empcolumn.deptno, 20).executedataset();
其中entity為雇員表emp對應的實體對象,這裡使用查詢表名的地方也可直接用實體對象。orderby預設不帶排序參數,隻執行個體化對象,也可帶排序字段,排序方式為升序;其後使用asc添加升序字段,desc添加降序字段。
、groupby分組條件及排序
這裡同樣以oracle自帶的scott使用者為例,先看如下代碼:
dbcore(databasetype.oracle,"password=tiger;user id=scott;data source=orac");
dst = dbcore.select().selectcolumnmaxvalue(empcolumn.empno)
.selectcolumnminvalue(empcolumn.empno).selectcolumnavgvalue(empcolumn.empno)
.from(empcolumn.tablename)
.groupby(empcolumn.deptno).column(empcolumn.sal)
.having().conditionandgreatequal(empcolumn.deptno,10)
.conditionandlessequal(empcolumn.deptno, 40).executedataset();
dst1 = dbcore.select().addmax(empcolumn.empno)
.addmin(empcolumn.empno).addavg(empcolumn.empno)
.having().conditionandgreatequal(empcolumn.deptno, 10)
dst = dbcore.select().selectcolumn(empcolumn.deptno).selectcolumn(empcolumn.sal)
.selectcolumnmaxvalue(empcolumn.empno).selectcolumnminvalue(empcolumn.empno)
.selectcolumnavgvalue(empcolumn.empno)
.conditionandlessequal(empcolumn.deptno, 40)
.orderby(empcolumn.deptno).asc(empcolumn.sal).asc("3").executedataset();
dst1 = dbcore.select().add(empcolumn.deptno).add(empcolumn.sal)
.addmax(empcolumn.empno).addmin(empcolumn.empno).addavg(empcolumn.empno)
.where().conditionandbetweenand(empcolumn.mgr, 7698, 7788)
.conditionandgreatequal(empcolumn.hiredate,new
datetime(1981, 5, 1))
.conditionandlessequal(empcolumn.hiredate,datetime.today)
.where().conditionandgreatequal(empcolumn.empno, 7654)
.groupby(empcolumn.deptno).column(empcolumn.sal).executedataset();
這裡dst、dst1變量分别對應同樣功能不同語句寫法的資料集資訊。同時展示了add替代selectcolumn;addmax、min、avg分别替代selectcolumnmax、min、avgvalue的示例代碼。where和groupby、having、orderby可同時使用,也可分開使用。最後三個展示了分别以where和groupby、having、orderby分開使用的例子。其中分組使用column(…)添加分組字段。可以很明顯的看出些語句的功能,和sql語句的抒寫幾乎一緻。如:倒數第四、五段的代碼(粗體部分,兩段代碼功能相同,隻是使用方法略異)換算成sql語句就是(假設今天是2009-10-11):
select emp.deptno, emp.sal,
max(emp.empno) as max_emp_empno, min(emp.empno) as min_emp_empno, avg(emp.empno) as avg_emp_empno
from emp
where emp.mgr between 7698 and 7788
and emp.hiredate >= to_date(‘1981-5-1‘, ‘yyyy-mm-dd‘) and
emp.hiredate <= to_date(‘2009-10-11‘, ‘yyyy-mm-dd‘)
group by emp.deptno, emp.sal
having emp.deptno >= 10
order by deptno asc, sal asc, 3 asc
和直接編寫sql很相似,省去許多代碼量,且可讀性高,維護也友善。
、結合事務處理的功能
(1)-(5)介紹的沒有加入事務處理功能,下面介紹結合事務的使用,先看如下代碼:
dbcore dbcore = newdbcore(databasetype.oracle,"password=tiger;user
id=scott;data source=orac");
dbcore.open();//--打開資料庫連接配接
dbcore.begintransaction();//開始事務
int intrecordcount
= dbcore.deletefrom(empcolumn.tablename).executenonquery();
dst = dbcore.selectall().from(empcolumn.tablename).executedataset();
dbcore.rollbacktransaction();//復原撤銷事務
//dbcore.committransaction();//送出事務
dst1 = dbcore.selectall().from(newemp()).executedataset();
dbcore.close();//--關閉資料庫連接配接
if (dbcore
!= null) dbcore.close();
messagebox.show(ex.message);
這裡仍然是以oracle自帶的scott使用者為例,其中的data source可以選擇任何oracle的服務名。這裡有必要說明的是使用了打開、關閉資料庫連接配接,因為要使用事務是以需要先打開資料庫連接配接,前文介紹沒有使用事務的,該步驟略過了。該例的功能是先删除emp表的所有資料再查詢該表的是以資訊,可看到在目前事務下是沒有資訊的,然後在復原事務,再查詢就又有資料了,這正是事務所起的作用。同時需要注意的是,資料庫連接配接打開後要關閉,且當中間出現異常時也應關閉已打開的資料庫連接配接,以釋放資源。
、分頁查詢(skip/take)
如下代碼所示:
dataset dstsqlite
= dbcore.select().add(entity.common.sqlite.t_demo.s_z)
.add(entity.common.sqlite.t_demo.s_a).add("b")
.from(entity.common.sqlite.t_demo.s_tablename)
.orderby().asc("z").skip(1).take(2).executedataset();
int countsqlite
= dstsqlite.tables[0].rows.count;
skip為跳過元素的數量,為連續取元素的數量。
、delete删除
仍然以oracle自帶的scott使用者為例,并結合事務處理,先看如下代碼:
dataset dst =new
dst = dbcore.selectall().from(newemp()).executedataset();
dbcore.begintransaction();//開始使用事務
emp entity
entity.empno
= 7782;
intrecordcount = dbcore.delete(entity);
intrecordcount = dbcore.deletefrom(empcolumn.tablename)
.where().conditionandequal(empcolumn.deptno, 10).executenonquery();
intrecordcount = dbcore.deletefrom(newemp()).executenonquery();
dbcore.rollbacktransaction();//復原結束事務
這裡先聲明一個emp雇員類的實體對象entity,并将主鍵empno賦以值7782,然後執行delete操作,即将主鍵為7782的記錄删除,接下來的查詢跟蹤運作可見到。下面的deletefrom方法,并結合where條件,将部門編号deptno等于10的雇員資訊全部删除,同樣再下面的查詢跟蹤運作可看到。最後将雇員表emp的記錄全部删除之,再查詢沒有任何資訊。之後復原事務,再次查詢,又有資料。關于使用實體對象插入(insert)、更新(update)、删除(delete)、儲存(save)操作可參見(上篇)或示例代碼。
、update更新
dbcore =
publicclass.getnewdbcore();
= 7499;
entity = entity.getentity(dbcore);
dbcore.update(empcolumn.tablename).set(empcolumn.sal,
entity.sal + 100)
.set(empcolumn.comm, entity.comm+ 100) .set(empcolumn.hiredate,datetime.today)
.where().conditionandequal(empcolumn.empno, 7499).executenonquery();
= dbcore.selectall().from(empcolumn.tablename)
.where().conditionandequal(empcolumn.empno, 7499).executedataset();//查詢
entity.sal)
.set(empcolumn.comm, entity.comm) .set(empcolumn.hiredate,
entity.hiredate)
.where().conditionandequal(empcolumn.empno, 7499).executenonquery();//恢複原值
dst = dbcore.selectall().from(empcolumn.tablename)
dbcore.committransaction();//送出事務
throw ex;
這裡先聲明一個emp雇員類的實體對象entity,并将主鍵empno賦以值7499,然後執行擷取資訊到對應的實體對象,查詢跟蹤運作可見到。之後分别将該員工的薪水sal和comm分别加100和将hiredate更新為今天,再查詢可見到剛剛更新的記錄資料,然後又将該員工的sal、 comm和hiredate更新恢複原值,跟蹤可見查詢的資料資訊,最後送出事務,該段程式隻是介紹示範之用。
、insert插入
用法同上,不再詳細贅述,這裡換張表以作差別,可見示例代碼,如下所示:
dbcore = getdbcore;
int count
= dbcore.insertinto(t_demo.s_tablename).values(t_demo.s_c_id,getkeyid)
.values(t_demo.s_c_name,"nameinsert").values(t_demo.s_c_idcard,"340221196606066066")
.values(t_demo.s_c_date,datetime.today).values(t_demo.s_c_int,10)
.values(t_demo.s_c_float,11.11).values(t_demo.s_c_eidtdate,datetime.now)
.executenonquery();
return count;
半自動化mapping-sql
、mapping-sql介紹
這裡的“半自動化”,是相對hibernate、eql等提供了全面的資料庫封裝機制的“全自動化”orm實作而言,“全自動”orm 實作了對象和資料庫表之間的映射,以及sql的自動生成和執行;而“半自動化”的着力點,則在于對象與sql之間的映射關系。也就是說,“半自動化”并不會為程式員在運作期自動生成 sql 執行,具體的sql需要程式員通過配置檔案編寫,然後加入sql所需的參數,以及将傳回的結果字段映射到指定的對象。這種方式在系統資料處理量巨大、性能要求極為苛刻需要高度優化sql語句的場景下,頗為優越;而一站式的“全自動化”方式則存在諸多不利。
、mapping-sql使用
半自動化mapping-sql方式,不同類型的資料庫隻需為對應類型的資料庫編寫對應的mapping-sql即可通過dbcore的接口統一調用執行,動态條件可以通過<[…]>的方式指定,即可按動态參數自動處理。即:動态條件以“<[”開始,以“]>”結束,成對出現,并附加參數在程式中動态加入。格式形如:
<?xmlversion="1.0"encoding="utf-8"
?>
<sqlmap>
<!--關鍵字 id區分大小寫
-->
<!-- query all information from table t_demo by condition. note: <[ ... ]>-->
<sqlid="lxchutao.mapingsql.demo.ui.query.test">
<content>
<![cdata[
select t.* from t_demo t
where 1=1 <[ and t.c_name=:c_name ]><[ and t.c_idcard like :c_idcard ]>
order by t.c_name
]]>
</content>
</sql>
</sqlmap>
這裡是oracle的寫法,可以看出與sql唯一的差別就是動态參數條件加了個<[…]>标記,其它同sql文法一樣。
mapping-sql配置資訊建議在系統初始化啟動時統一加載到記憶體,執行時以id進行擷取。即在全局應用程式類global.asax的application_start執行以下代碼:
//程式啟動前預設加載所有naming-sql檔案到記憶體
string defaultpath
= hostingenvironment.applicationphysicalpath+
"config\\db\\"+ dbfilename()
+ "\\";
foreach (string filenamein
directory.getfiles(defaultpath))
if (filename.tolower().endswith("maping-sql.xml"))
mapingsqlutil.load(filename);
示例(見安裝後附件mapingsql.demo)代碼中mapping-sql檔案放置于ui根目錄
(\config\db\[oracle]|[sqlserver][access][sqlite]\*.maping-sql.xml)。
代碼中使用格式示例如下:
using system.data;
using system.database;
namespace lxchutao.mapingsql.demo.bll
public
class demo
///<summary>
///查詢示例:根據條件mapingsql會自動比對查詢動态條件(<[ ... ]>)(沒有加這個前後标記的會一直參與查詢執行)
///當對應的參數存在有值時,該條件才會參與查詢(增删改參數條件語句于此類似)
///</summary>
///<param name="name">精确比對參數名稱</param>
///<param name="idcard">模糊比對idcard</param>
///<returns>查詢結果集</returns>
public
dataset gett_demobycondition(string name,string idcard)
{
using (dbcore dbcore=
dbhelper.getnewdbcore())
{
dbcommandwrapper cmd= dbcore.getmapingsqlcommandwrapper("lxchutao.mapingsql.demo.ui.query.test");//隻需mapingsql的id
if (!string.isnullorempty(name)) cmd.addinparameter("c_name",dbtype.ansistring,
name);
if (!string.isnullorempty(idcard)) cmd.addinparameter("c_idcard",dbtype.ansistring,"%"
+ idcard +
"%");
return dbcore.executedataset(cmd);
}
}
存儲過程
誠然orm并不是萬能的,當遇到特别複雜的資料處理、海量資料運算、性能瓶頸優化或彌補設計的不足時(盡管不是很多,但也很重要),還應歸結到sql語句或存儲過程來實作才是好的選擇。實際項目中仍然會有海量複雜的資料處理或複雜子查詢、sql語句優化和存儲過程等,dbcore提供了良好的解決方案,很好的解決了項目中10%-20%的那部分功能。該元件支援目前市場上ado.net支援的各種類型的資料庫,可執行自定義編寫的sql語句和存儲過程等,可針對複雜功能的特殊處理并對項目瓶頸問題作性能優化等操作。
system.database.dbcore可直接用于oracle、sqlserver、sybase、db2、mysql、sqlite、postgresql、access、dm(達夢)、informix、firebird、maxdb和支援oledb、odbc連接配接類型的資料庫,執行個體代碼分别如下:
dbcore(databasetype.oracle, “oracleconnectionstring”);
dbcore(databasetype.odpnet, “oracleconnectionstring”);//odp.net方式
dbcore(databasetype.sqlserver, “sqlserverconnectionstring”);
dbcore(databasetype.sybase, “sybaseconnectionstring”);
dbcore(databasetype.db2, “db2connectionstring”);
dbcore(databasetype.mysql, “mysqlconnectionstring”);
dbcore(databasetype.sqlite, “sqliteconnectionstring”);
dbcore(databasetype.postgresql, “postgresqlconnectionstring”);
dbcore(databasetype.access, “accessconnectionstring”);
dbcore(databasetype.dm, “dmconnectionstring”);
dbcore(databasetype.informix, “informixconnectionstring”);
dbcore(databasetype.firebird, “firebirdconnectionstring”);
dbcore(databasetype.maxdb, “maxdbconnectionstring”);
dbcore(databasetype.oledb, “oledbconnectionstring”);
dbcore(databasetype.odbc, “odbcconnectionstring”);
、dbcore+sql
下面看一段适合oracle和sqlserver通路的通用sql代碼:
= publicclass.getnewdbcore();
string strparatoken
= dbcore.getcurrentparametertoken; //對應資料庫的參數前導符
string strsql
= "insert into dept (deptno, dname, loc) values ("+ strparatoken
+ "deptno, "
+ strparatoken +
"dname, " + strparatoken+
"loc)";
dbcore.open();
//打開資料庫連接配接
dbcore.begintransaction();
//開始事務
dbcommandwrapper cmd
= dbcore.getsqlstringcommandwrapper(strsql);
//cmd.addparameter(..);//為指令增加一個參數執行個體
cmd.addinparameter(strparatoken
+ "deptno",
dbtype.int32, 99);
+ "dname",
dbtype.string,
"部門名稱");
+ "loc", dbtype.ansistring,"loctest");
int intmaxdeptid
= dbcore.getint32maxid("dept","deptno");//目前表的deptno最大值
dbcore.executenonquery(cmd);
intmaxdeptid = dbcore.getint32maxid("dept","deptno");//插入資料deptno=99之後目前表的deptno最大值
strsql =
"delete dept where deptno = " + strparatoken+
"deptno";
dbcommandwrapper cmd1
cmd1.addinparameter(strparatoken
dbcore.executenonquery(cmd1);
intmaxdeptid = dbcore.getint32maxid("dept","deptno");//删除資料deptno=99之後目前表的deptno最大值
dbcore.rollbacktransaction();//復原撤銷事務。等于該方法什麼都沒做,隻是示範作用
intmaxdeptid = dbcore.getint32maxid("dept","deptno");
dbcore.close();//關閉資料庫連接配接
其中第一句的publicclass.getnewdbcore()方法體代碼可以是newdbcore(databasetype.oracle,
“oracleconnectionstring”)也可以是newdbcore(databasetype.sqlserver, “sqlserverconnectionstring”),當new的是oracle時即表示操作通路的是oracle資料庫,當new的是sqlserver即表示操作通路的是sqlserver資料庫。dbcore.getcurrentparametertoken即是擷取對應資料庫連接配接參數的前導符(如:oracle是“:”,sqlserver是“@”等),這裡也可以結合使用dbcore.standardsqlwithparameters方法對目前帶參數的sql語句進行标準化通用處理,即所寫sql可以用于如mysql/access資料庫等。這裡的資料庫操作同樣也是可以同實體對象一塊協同工作。dbcore.getsqlstringcommandwrapper(…)建立一個sql語句的指令,dbcore.getstoredproccommandwrapper(…)建立一個執行存儲過程的指令,可根據項目自身實際需要選擇使用。
對專有資料庫指令也可以轉化為指定資料庫指令來使用,這樣可針對該資料庫特性使用更多的方法,如oracle、sqlserver的指令轉化可像下列代碼來轉化:
oraclecommandwrapper cmd
= dbcore.getsqlstringcommandwrapper(strsql)as
oraclecommandwrapper;
sqlcommandwrapper cmd
sqlcommandwrapper;
不帶參數sql語句的快捷用法
dst
= dbcore.executedataset(strsql);
datatable dtbl = dbcore.executedataset(strsql).tables[0];
這裡順便說明一下,當程式執行出現異常時可使用dbcore.close()來關閉目前打開的資料庫連接配接(當然如果不顯式關閉連接配接,dbcore在執行個體銷毀時檢測到該執行個體未關閉連接配接也會自動關閉,但這樣無疑一直占據着資源,不推薦,建議及時關閉及時釋放資源),如下代碼所示:
最後再說一個system.database.dbcoreconnectlimit.alldbmaxconnectioncount,可以設定資料庫打開的最大連接配接數目,預設不受限制。
、dbcore+存儲過程
dbcore+存儲過程标準用法:
= dbcore.getstoredproccommandwrapper("[包名.]存儲過程名");
//cmd.addinparameter(...
= dbcore.getstoredproccommandwrapper("[包名.]存儲過程名")as
//cmd.addoutparameter(...
//cmd.addparameter(...
//cmd.addcursoroutparameter(...
dbcore.executedataset(cmd);
同時dbcore也提供了對存儲過程的一個快捷用法:
dbcore.executestoredprocedure("[包名.]存儲過程名");
下面就以oracle為例,看下dbcore+存儲過程的具體寫法:
string strconnection
dbcore dbcore
int count;
oraclecommandwrapper cmd
= dbcore.getstoredproccommandwrapper("storedprocedure.namea")as
//cmd.addinparameter(
//cmd.addoutparameter(
//cmd.addparameter(
//cmd.addcursoroutparameter(
count = dbcore.executenonquery(cmd);
cmd = dbcore.getstoredproccommandwrapper("storedprocedure.nameb")as
count += dbcore.executenonquery(cmd);
//count += dbcore.executestoredprocedure("storedprocedure.namec");
if (dbcore.istransaction) dbcore.rollbacktransaction();
、dbcore執行sql/存儲過程的快捷用法
如下所示:
= new dbcore(myoracle.baseentity.getconnectionstring());
= dbcore.createsql("select * from emp where deptno = :deptno")
.addinparameter("deptno",dbtype.int32, 20).executedataset();
int intcount
= dbcore.createstoredprocedure("[包名.]存儲過程名")
.addinparameter("deptno",dbtype.int32, 20).executenonquery();
輔助擴充功能
許多工具都提供例外輔助的功能,該工具也不例外,簡要介紹如下:
、tablehelp輔助擴充
以oracle自帶的scott使用者為例,先看如下代碼:
datatable dt1
= dbcore.selectall().from(empcolumn.tablename).executedataset().tables[0];
datatable dt2
= dbcore.selectall().from(deptcolumn.tablename).executedataset().tables[0];
datatable dt3
= tablehelp.mergetable(dt1, dt2,"deptno");//按部門編号deptno列将表dt2的資料合并到dt1
datatable dt3_
= tablehelp.mergetable(dt2, dt1,"deptno");//按部門編号deptno列将表dt1的資料合并到dt2,dt1中有多行資料對應,取首行的資料,沒有對應的資料為空
datatable dt4
= tablehelp.addtablerownumcol(dt3); //給dt3添加行号
datatable dt5
= tablehelp.gettabletoprows(dt4, 5); //擷取前5行
datatable dt6
= tablehelp.gettablesubrows(dt4, 6, 10); //擷取dt4從第6行到第10行
datatable dt7
= tablehelp.gettablesubrows(dt4, 11, 20);//擷取dt4從第11行到第20行,注:無20行取到最後一行
datatable dt8
= tablehelp.gettablebottomrows(dt4, 5); //擷取dt4後5行
dt8 =
tablehelp.gettablebottomrows(dt4, 50); //擷取dt4後50行;dt4沒有後50行,從後面往前取到最前面存在行
datatable dt9
= tablehelp.joininner(dt1, dt2,"deptno");
//内連接配接
datatable dt10
= tablehelp.joininner(dt1, dt2,"deptno");
datatable dt11
= tablehelp.joinleft(dt1, dt2,"deptno");
//左外連接配接
datatable dt12
= tablehelp.joinright(dt1, dt2,"deptno");
//右外連接配接
datatable dt13
= tablehelp.joinleft(dt2, dt1,"deptno");
datatable dt14
= tablehelp.joinfull(dt1, dt2,"deptno");
//完全外連接配接
datatable dt15
= tablehelp.sorttable(dt1,"deptno",
sortdirection.asc);
datatable dt16
= tablehelp.sorttable(dt1,"deptno");
datatable dt17
sortdirection.asc,"sal",sortdirection.asc);
datatable dt18
= tablehelp.sorttable(dt1,"deptno","sal");
datatable dt19
sortdirection.desc,"sal",
sortdirection.desc);
datatable dt20
= tablehelp.sorttabledesc(dt1,"deptno",
"sal");
datatable dt21
sortdirection.asc,"sal",
tablehelp.datatabletoexcel(dt1,@"c:\documents and settings\楚濤\桌面\temp1.xls");
datatable dt22
= dt1.copy();
//修改表名,datatable預設tablename="table",dataset集合的datatable.tablename不能同名
dt22.tablename
= "emp";
datatable dt23
= dt2.copy();
dt23.tablename
= "dept";
dst.tables.add(dt22);
dst.tables.add(dt23);
tablehelp.datasettoexcel(dst,@"c:\documents and settings\楚濤\桌面\temp2.xls");
tablehelp輔助擴充類,提供合并datatable資料表、擷取資料表指定行的資料、内連接配接、左外連接配接、右外連接配接、完全外連接配接、對資料表排序、将資料表datatable或資料集dataset輸出到excel檔案等,可見上述程式代碼的釋義說明文字。
同時也提供通過過濾條件選擇datatable行、合并資料表行資訊、轉換資料表列值對并以datatable的形式傳回的常用方法,如下所示:
datatable dt24
= tablehelp.gettableselect(dt1,"deptno=10");//選取deptno=10的所有資訊,并以datatable的形式傳回
datatable dt25
= tablehelp.gettableselect(dt1,"deptno=20");//選取deptno=20的所有資訊,并以datatable的形式傳回
datatable dt26
= tablehelp.tableappend(dt24, dt25);//将dt23資料按行附加到dt22,并以新的結果資料表的形式傳回
string[,] strarray
= new string[1, 2];
strarray[0, 0] =
"scott";
strarray[0, 1] =
"scott/tiger";
datatable dt27 =tablehelp.replacletablecolvalue(dt26,"ename", strarray);
mergetable和tableappend差別是,前者是橫向合并,即列的合并;後者是縱向合并,即行的合并。replacletablecolvalue功能既是如字面意思所示替換table列值,沒有比對的保留原值,功能類似oracle的decode函數。
、commonhelp常用方法擴充
commonhelp提供了許多常用方法,如财務上人民币金額大寫的轉換、字元串中中文的檢查、唯一随機數字固定長度為20的數字字元串的産生、html代碼和對應格式化的字元串的互相轉化、整型和浮點型數字字元串檢查、email位址和日期時間格式字元串的檢查、獲得中文字元串的漢語拼音碼首字母等常用功能,可見以下示例:
string str1
= commonhelp.numbertormb(1); //"壹元整"
str1 =
commonhelp.numbertormb(102);
//"壹佰零貳元整"
commonhelp.numbertormb(1000234);
//"壹佰萬零貳佰叁拾肆元整"
commonhelp.numbertormb(1000023456);
//"壹拾億零貳萬叁仟肆佰伍拾陸元整"
commonhelp.numbertormb(100000234567);
//"壹仟億零貳拾叁萬肆仟伍佰陸拾柒元整"
decimal dec
= 1234007890123.45m;
commonhelp.numbertormb(dec);
//"壹萬貳仟叁佰肆拾億零柒佰捌拾玖萬零壹佰貳拾叁元肆角伍分"
string str
= string.empty;
for (int i= 0; i
< 1000; i++)
str +=
commonhelp.getonlyid()
+ "\r\n";//唯一随機數字固定長度為20的數字字元串
messagebox.show(str);
str =
commonhelp.getid20();//唯一随機數字固定長度為的數字字元串
commonhelp.getid25();//唯一随機數字固定長度為的數字字元串
commonhelp.getid30();//唯一随機數字固定長度為的數字字元串
= "abcdefgh";
bool ishaschinese
= commonhelp.ishaschineseword(str1); //false不含有中文字元
"abcd啊efgh";
ishaschinese = commonhelp.ishaschineseword(str1); //true含有中文字元
還有其他常用方法和加密/解密常用方法擴充類cryptographyhelp、officehelp常用方法輔助擴充類等就不在此一一列舉了。
的分析及與xml、json、estring的互動
、orm的分析
orm通過對開發人員隐藏sql細節可以大大提高生産力。然而很不幸,“orm”和“性能問題”常常一起出現,它容易産生一些未被發現的荒謬查詢,雖然幾率不是很高,但萬一出現,如果沒有好的分析方案,也是極為頭疼的事。通常情況下,資料庫管理者可以通過如交叉引用有問題的存儲過程或其它途徑來查找問題代碼。但是,orm依賴于動态生成的sql,便很難這麼做了。是以,需要一些有效的orm分析方法。該工具元件system.database.dbcore提供了dbcore.getcurrentcommandtext等靜态方法可以在任何執行orm操作時分析目前執行的sql語句,這樣在任何時候當出現異常時可檢視目前執行的sql,如果sql正常可排除不是orm的問題,否則說明所編寫的代碼結構中出現了問題。
、與xml的互動
工具生成的實體提供toxml和fromxml兩個方法及相應的重載和補充方法,可以友善實體對象與xml内容直接互相轉換。
示例代碼如下所示:
tstentity entity
= new tstentity(1); //得到主鍵為1的實體對象資訊
tstentity entity_
= new tstentity();//僅執行個體化實體對象
string stst
= entity.toxml();
entity.toxml(@"c:\tst.xml",encoding.utf8,
formatting.indented);
entity.toxml_(@"c:\tst1.xml",encoding.utf8,
entity_ = entity_.fromxml(stst);
entity_ =
new tstentity();
entity_ = entity_.fromxmlfile(@"c:\tst.xml");
entity_ = entity_.fromxmlfile(@"c:\tst1.xml");
同樣實體集也提供toxml和fromxml及相應的重載和補充方法,友善實體集對象與xml内容直接互相轉換。
employees entitys1 =
new employees(true); //擷取員工對象的所有資訊到實體集對象
employees entitys2 =
new employees();//僅執行個體化實體集對象
employees entitys3 =
new employees();
employees entitys4 =
string strxml
= entitys1.toxml();
entitys2 = entitys2.fromxml(strxml); //從xml内容中加載對象
strxml = entitys1.toxml(formatting.indented);
entitys3 = entitys3.fromxml(strxml); //從xml内容中加載對象
string strfile
= "temp.xml";
entitys1.toxml_(strfile,
encoding.utf8, formatting.indented);
entitys4 = entitys4.fromxmlfile(strfile); //從xml檔案中加載對象
、與json的互動
工具生成的實體提供tojson和fromjson兩個方法及相應實體集方法,可以友善實體對象、實體集對象與json内容直接互相轉換。
employee e1
= new employee("a-c71970f");//得到主鍵為a-c71970f的對應資料
employee e2
= new employee("asd");//沒有對應資料
employees es
= new employees(true);//擷取全部資料
employee entity1;
employees entitys;
employees entitys1;
employees entitys2;
entity1 =
new employee();
string json1
= e1.tojson();//将實體資訊轉化為json文本
entity1 = entity1.fromjson(json1);//從json文本中轉化到實體
entitys =
entitys1 =
entitys2 =
entitys.add(e1);
entitys.add(e2);
string json3
= entitys.tojson();
entitys1 = entitys1.fromjson(json3); //通用用法
entitys2.fromjson(json3); //實體集專用用法
json3 = es.tojson();
、與string(即estring)的互動
工具生成的實體提供tostring和fromstring兩個方法及相應實體集方法,可以友善實體對象、實體集對象與string内容直接互相轉換。
employee entity1
= new employee();
employees entitys1
= new employees();
employees entitys2
= e1.tostring();//轉化為字元串文本
entity1 = entity1.fromstring(str1);//從字元串文本中執行個體化到實體
string strentitys
= es.tostring();
entitys1 = entitys1.fromstring(strentitys);//實體集通用用法
entitys2.fromstring(strentitys);//實體集專用用法
注:estring是針對實體/集(亦即一維/二維資料)設計的,格式為鍵值對的形式出現,以鍵盤不能直接輸入的單個生僻字元’┋’為分割符,對于集合類,以’┋┋’(即連續兩個’┋’)為分割符(示例=>name┋value1┋┋name┋value2)。
的支援
、linq toentitys
實體集對象預設是可以支援linq的,隻是linq是在.net framework 3.5 中出現的技術,是以在建立新項目的時候必須要選3.5或者更高版本,才能使用。目前項目基本上都是.net3.5或以上版本的,這裡也把這部分示例代碼加上:
// entitys linq 的查詢示例 net framework 3.5/4.0/及其以上
tstentitys entitys
= new tstentitys();
= new tstentity();
entity.tsttxt
= "aa";
entitys.add(entity);
entitys.add(newtstentity() { tsttxt
="bb" });
var qq = entitys.oftype<tstentity>();
var pp =from p
in qq where p.tsttxt
=="aa"
select p;
foreach (var pin qq)
string s
= p.tsttxt; //兩個結果分别是:aa、bb
foreach (var pin pp)
string s
= p.tsttxt; //篩選後的結果:aa
可以看出關鍵是這句entitys.oftype<tstentity>(),oftype是system.linq下的方法,需要引用該命名空間。選擇3.5或更高版本的.net framework之後,建立的新項目中會自動包含system.linq的命名空間(using system.linq;)。
輕量級日志元件
經常,客戶說報錯的時候,作為開發人員,基本上不可能到第一現場觀察錯誤提示,這也導緻了異常定位非常的困難,大大增加了維護的成本。使用日志後,一旦某個頁面出錯,即使客戶沒有回報,系統也能記錄下來,為管理者修正錯誤以及優化系統提供足夠豐富的資訊。ecg同樣也提供了輕量級的簡單日志元件。
、簡單日志配置
配置說明如下:
配置節關鍵字
配置說明
_log_level_
日志級别配置節關鍵字,配置内容可為:debug|info|warn|error|fatal
(預設error)
_log_file_
日志檔案配置節關鍵字配置内容:日志檔案路徑+檔案名(不含檔案擴充名)(如果配置節_log_file_未配置,則取環境變量"_log_env_path_file_"對應的配置值;如果該環境變量值也未配置則取環境變量%temp%臨時目錄對應的目錄下;如果環境變量%temp%也未配置,則取運作程式所在運作路徑,預設檔案名simplelogger)
_log_file_max_size_
日志檔案配置節關鍵字,日志檔案的最大長度(機關:位元組)(不得小于1024位元組,即1k)(預設1m = 1*1024*1024=1048576)
當日志檔案超過該大小時會自動重建。
_log_split_
日志檔案是否按命名空間分開存儲的配置節關鍵字: true|false (預設false)
_log_ns_maxlength_
當日志檔案按命名空間分開存儲時(即_log_split_=true),命名空間截取的最大長度(該值必須大于0,方有效),預設命名空間全稱
_log_ns_div_count_
當日志檔案按命名空間分開存儲時(即_log_split_=true),命名空間長度最大截取到第幾分段(以“.”分割)(該值必須大于0,方有效),預設命名空間全稱
_log_asyn_
異步寫入日志标示,true:新啟線程采用日志隊列方式異步寫入,false:直接寫入日志檔案(預設false)
_log_asyn_wait_
異步寫入時,當日志隊列為空的等待毫秒數(必須大于等于0,為0則沒有等待時間(較耗資源);可不配置,預設100毫秒)(_log_asyn_必須配置true,該項才起作用)
_log_asyn_thread_background_
異步寫入線程是前台線程還是背景線程(對應thread.isbackground屬性),true背景線程,false前台線程(預設false)(_log_asyn_必須配置true,該項才起作用)
這裡的配置節關鍵字是區分大小寫的,要求全部大寫,所有配置項都有預設值,不需要全部配置,可根據實際需要進行選擇。配置項在系統首次加載時進行初始化,再次修改配置項需重新開機應用程式。
日志路徑預設為運作程式所在運作路徑,對于web程式,若在visualstudio下web.config中沒有配置_log_file_指定到具體路徑下,則會在對應的ide目錄下(以vs2005為例,在microsoft visual studio8\common7\ide\);建議這裡明确填寫具體完整的絕對路徑(例如:c:\log\simplelogger,日志記錄到c:\log\目錄下(如果目錄不存在則會自動建立),日志檔案名以simplelogger為字首開頭);日志檔案的擴充名為(.log)。
如果配置節"_log_file_"未配置,則取環境變量"_log_env_path_file_"對應的配置值(即%_log_env_path_file_%);如果該環境變量值也未配置則取環境變量"temp"臨時目錄(%temp%)對應的目錄下;如果環境變量(%temp%)也未配置,則預設為運作程式所在運作路徑。
注:這些配置項亦可通過系統環境變量指定的鍵值對(key=value)配置檔案設定,環境變量關鍵字"_log_env_config_file_",該值指向鍵值對配置檔案所在的全路徑。當該環境變量未設定或指定的檔案不存在,則讀取應用程式配置檔案web.config或app.config,查找對應的配置節,若這裡也未配置則取各配置項的預設值。
、簡單日志的使用
using system.logger;
simplelogger logger =simplelogger.getinstance();
logger.debug("環境列印出的調試日志");
logger.info("環境列印出的資訊日志");
logger.warn("環境列印出的警告日志");
logger.error("環境列印出的一般錯誤日志");
logger.fatal("環境列印出的緻命錯誤日志");
日志元件位于system.database.dll中,項目引用命名空間(system.logger)即可,日志元件提供五種級别的日志記錄(debug|info|warn|error|fatal),最低debug,最高fatal,當配置的級别大于等于對應級别時,會自動記錄日志,如:目前配置_log_level_值為error時,debug|info|warn三種級别日志将直接跳過不記錄,error|fatal級别的日志會記錄。simplelogger提供了單執行個體接口simplelogger.getinstance(),支援多線程并發操作,使用時可直接調用擷取日志執行個體對象。同時各級别日志記錄接口(debug|
info warn| error| fatal)均有不同重載方式。
以上就是vb/c#.net實體代碼生成工具(entityscodegenerate)對各種資料庫通路的統一操作的簡單介紹,詳細可見示例代碼及工具幫助文檔;工具安裝後附帶大量的示例代碼,裡面有更多的示例和chm格式的幫助文檔。另外,使用該工具,當項目需要切換資料庫或使用不同的資料庫時,隻須适當修改資料庫連接配接類型及連接配接字元串即可。
在設計表時建議:資料庫表名及字段名應當遵循通用規範,如都使用大寫英文字母,首字元以大寫英文字母開頭且不含有特殊符号,如‘┋‘字元;表中不含有嵌套表、表名不與字段名重複(對老系統如果表名與字段名相同,則可通過建立視圖或同義詞再修改實體類名也可,但一般不推薦這樣使用)等。另外文字命名可約定俗成為單數形式,可以是英文字母及數字和下劃線(_)組合,此外建議表命名以"t_"開頭,表字段以"c_"開頭,視圖以"v_"開頭等,這樣做的目的可使資料庫設計更規範化,也避免了與關鍵字的沖突,同時也是因為許多資料庫都是英文字母開頭的且不能含有特殊字元,如oracle就是這樣,且表名稱和字段名都是大寫(對postgresql建議采用小寫命名),長度不超過30個字元(命名過長無意義,有些資料庫也不支援),多出的資訊可以在命名注釋裡多寫些注釋資訊。若采用oledb、odbc方式連接配接,表字段名最好不要這樣field1,2,3...以數字順序結尾,因為這樣容易與參數機制沖突,導緻一些資料通路時帶來一些參數上不必要的麻煩,且這樣命名也不是好的規範,必要時可以是fielda,b,c...。每個表建議都設定主鍵,可以是聯合主鍵。若沒有主鍵,則在使用主鍵判斷記錄的唯一性時須指定字段。
目前也有許多類似的工具(如linq to sql)等,但總有許多限制:如linq to sql隻在.net3.5之上才能使用,僅用于sqlserver;其它也有許多相關不足,如:對國産資料庫及自定義資料庫的不支援、無主鍵或聯合主鍵的不支援、大量複雜的配置、屬性字段注釋提取的缺少、運作不穩定、生成代碼可讀性差、同一項目不支援多資料庫的整合、需要為不同資料庫學習不同的程式設計方式、内部配置檔案複雜或報錯、工具相容性差、缺少orm分析或其它使用中及生産環境運作的問題等等。該工具很好的解決了這些問題,支援.net1.1及以上版本,并結合多年具體項目的需求,可以像抽屜一樣單獨或分開使用,也可和其它元件并行使用,如可以與其它元件或平台的內建,動态得從相關平台或元件中讀取資料庫參數而不必寫死在檔案裡,這樣若要實作對使用者資料庫連接配接資訊加密/解密也可以很好的處理。
理論的實作總是從簡單到複雜,覆寫所有可能,實際應用則需要結合實際從複雜到簡單,凡是要靈活變通使用,化複雜為簡單,将複雜的東西以簡單的方式處理。
軟體開發都希望從大量重複的代碼中解放出來,縮短工期并提高項目品質,同時當需要的時候可以準确的将絕大多數背景代碼快速完成,并要便于後期維護,這些就需要适當借助一些工具和方法為開發、維護和交流提供良好的保障,顯然該工具提供了最佳選擇。
盡管我們注入大量心血,但不足之處在所難免,有待進一步完善,敬請來信交流()、批評斧正!