.NET ORM 的 “SOD蜜”--零基礎入門篇
PDF.NET SOD框架不僅僅是一個ORM,但是它的ORM功能是獨具特色的,我在部落格中已經多次介紹,但都是原理性的,可能不少初學的朋友還是覺得複雜,其實,SOD的ORM是很簡單的。下面我們就採用流行的 Code First的方式,一步步來了解下。
一、準備工作
1.1,新增SOD包引用
首先建立一個控制檯專案(支援.NET2.0的專案),並使用程式包管理器新增PDF.NET SOD的程式引用:
PM> Install-Package PDF.NET.SOD
更多詳細使用資訊說明,請參考nuget 網站說明 https://www.nuget.org/packages/PDF.NET/
1.2,配置資料連線
新建一個控制檯專案,新增一個應用程式配置檔案,增加一個數據庫連線配置:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="local" connectionString="Data Source=.;Initial Catalog=LocalDB;Integrated Security=True" providerName="SqlServer" /> </connectionStrings> </configuration>
上面的連線字串要求你本地已經安裝SqlServer,框架相容2000以上所有版本,有一個數據庫名字是 LocalDB。當然你也可以修改成你實際的連線字串。
之後,我們的查詢示例,都將採用這個連線配置。
注意:最新版本的SOD框架,如果使用的是SqlServer,並且連線字串指定了資料庫名字但實際上沒有這個資料庫,框架可以自動建立資料庫,此功能需要SOD的Code First功能支援,請參考下面“1.5 ,建立資料庫表”的功能。
SOD框架最基本的配置,僅需要這一個地方,這比起EF來說要簡單。
如果是SqlServer +EF Code First方式的連線配置,SOD框架也可以使用它這個連線字串的。
1.3,定義實體類
我們將使用最常見的使用者登入來舉例,這裡需要一個使用者實體類,假設它的定義是下面這個樣子的:
public class User : EntityBase
{
public User()
{
TableName="Tb_User";
IdentityName = "UserID";
PrimaryKeys.Add("UserID");
}
public int ID
{
get { return getProperty<int>("UserID"); }
set { setProperty("UserID", value); }
}
public string Name
{
get { return getProperty<string>("Name"); }
set { setProperty("Name", value, 50); }
}
public string Pwd
{
get { return getProperty<string>("Pwd"); }
set { setProperty("Pwd", value, 50); }
}
public DateTime RegistedDate
{
get { return getProperty<DateTime>("RegistedDate"); }
set { setProperty("RegistedDate", value); }
}
}
在上面的定義中,建構函式內指明瞭 IdentityName = "UserID";這表示當前實體類對應的表有一個叫做 UserID的自增列,每當插入實體類後,可以通過該自增列對應的屬性獲取到新插入的自增ID的值。
另外,我們發現每個實體類的屬性中,都是這樣的寫法:
public int ID
{
get { return getProperty<int>("UserID"); }
set { setProperty("UserID", value); }
}
屬性內 getProperty ,setProperty 方法內的第一個引數,就是屬性對應的欄位名字。 所以,這樣的屬性,SOD框架稱為“持久化屬性”。
可以看到,SOD實體類還是比較簡單的,它沒有使用特性來申明資料庫資訊,這意味著你可以在執行時修改實體類影射的主鍵,自增欄位,表名稱等資料庫元資料,並且不需要反射,這些特性構成了SOD框架簡單而強大的基礎。
1.4,新增查詢物件的資料上下文
在專案中新增一個 LocalDbContext.cs 檔案,檔案中新增如下程式碼,以便檢查表 Tb_User 是否存在,如果不存在,則自動建立一個:
/// <summary>
/// 用來測試的本地SqlServer 資料庫上下文類
/// </summary>
public class LocalDbContext : DbContext
{
public LocalDbContext()
: base("local")
{
//local 是連線字串名字
}
#region 父類抽象方法的實現
protected override bool CheckAllTableExists()
{
//建立使用者表
CheckTableExists<User>();
return true;
}
#endregion
}
本例中使用SqlServer 作為查詢示例,程式會自動使用 SqlServerDbContext 密封類來進行內部協作處理,但這取決於你的AdoHelper例項型別,如果是Oracle,則內部使用的是OracleDbContext 密封類。
如果你不需要ORM的 Code First功能,這個 DbContext 實現類是不需要寫的。
1.5,建立資料庫表
OK,基本準備就緒,現在Main 方法裡面可以寫下面的程式碼開始工作了:
LocalDbContext context = new LocalDbContext();//自動建立表
準備工作全部完成,執行這一句程式碼,之後檢視SqlServer管理工具,發現表 Tb_User 已經建立了。
二、ORM之增,刪,改
SOD框架的ORM功能跟通常的ORM框架不同,SOD框架的實體類上並沒有資料查詢和持久化的方法,所以SOD的實體類是“非常純粹的”實體類,你可以把它看作是一個數據容器,或者用來當作DTO,ViewModel使用,有關這個話題更詳細的闡述,請看這篇文章:《DataSet的靈活,實體類的方便,DTO的效率:SOD框架的資料容器,打造最適合DDD的ORM框架》。
在進行真正的資料查詢之前,得先有資料,所以我們先測試資料的增刪改。
2.1,刪除資料
首先刪除之前的測試資料,可以採用OQL進行批量資料刪除:
int count = 0;
//刪除 測試資料-----------------------------------------------------
User user = new User();
OQL deleteQ = OQL.From(user)
.Delete()
.Where(cmp => cmp.Comparer(user.ID, ">", 0)) //為了安全,不帶Where條件是不會全部刪除資料的
.END;
count= EntityQuery<User>.ExecuteOql(deleteQ, context.CurrentDataBase);
這裡將使用者ID 大於0的資料全部刪除了,框架內建了資料安全機制,不會隨意刪除全部資料,所以為了清除全部資料,採用了上面的方法。
2.2,增加資料
方式1,採用DbContext 的Add 方法:
count = 0;
User zhang_san = new User() { Name = "zhang san", Pwd = "123" };
count += context.Add<User>(zhang_san);//採用 DbContext 方式插入資料
方式2,採用OQL:
User li_si = new User() { Name = "li si", Pwd = "123" };
OQL insertQ= OQL.From(li_si)
.Insert(li_si.Name);//僅僅插入使用者名稱,不插入密碼
OQL還需EntityQuery 物件配合,才可以執行,繼續看下面的程式碼:
AdoHelper db = MyDB.GetDBHelperByConnectionName("local");
EntityQuery<User> userQuery = new EntityQuery<User>(db);
count += userQuery.ExecuteOql(insertQ);
下面是結果圖:
方式3,直接EntityQuery 插入實體類:
User zhang_yeye = new User() { Name = "zhang yeye", Pwd = "456" };
count += EntityQuery<User>.Instance.Insert(zhang_yeye);//採用泛型 EntityQuery 方式插入資料
2.3,修改資料:
方式1,採用 DbContext 方式更新資料
li_si.Pwd = "123123";
count = context.Update<User>(li_si);//採用 DbContext 方式更新資料
方式2,採用OQL方式更新指定的資料
li_si.Pwd = "123456";
OQL updateQ= OQL.From(li_si)
.Update(li_si.Pwd) //僅僅更新密碼
.END;
count += EntityQuery<User>.Instance.ExecuteOql(updateQ);//採用OQL方式更新指定的資料
方式3,採用泛型 EntityQuery 方式修改資料
li_si.Pwd = "888888";
count += EntityQuery<User>.Instance.Update(li_si);//採用泛型 EntityQuery 方式修改資料
三、ORM之資料查詢
前面增刪改資料完成了,現在有了測試資料,我們可以來進行ORM的資料查詢測試了,這裡使用使用者登入的例子來測試,框架提供了6種資料查詢方式。
3.1,最簡單的方法
假設前端直接傳遞了一個 User 實體類物件,中間設定了使用者名稱和密碼,現在有一個登入方法使用該物件,該方法詳細內容如下所示:
/// <summary>
/// 使用使用者物件來登入,OQL最簡單最常見的使用方式
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public bool Login(User user)
{
OQL q = OQL.From(user)
.Select()
.Where(user.Name, user.Pwd) //以使用者名稱和密碼來驗證登入
.END;
User dbUser =q.ToEntity<User>();//ToEntity,OQL擴充套件方法
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
這裡我們使用了SOD框架的ORM查詢語言--OQL,它的結構非常類似於SQL,你可以認為OQL就是物件化的SQL語句。
OQL表示式採用 .From.....END 的語法,物件的鏈式方法呼叫,只要敲點出來的就是正確的,這樣沒有SQL基礎的同學也可以很快掌握該查詢語法,也能馬上作資料開發。
注意:在本例中,使用了OQL的擴充套件方法,因此需要引用下面的名字空間:
using PWMIS.Core.Extensions;
如果不使用擴充套件方法,可以採用泛型EntityQuery 的方法,請看下面的示例。
3.2,使用 OQLCompare 物件的 EqualValue 相等比較方式
/// <summary>
/// 使用使用者物件來登入,但是使用 OQLCompare 物件的 EqualValue 相等比較方式
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public bool Login1(User user)
{
OQL q = OQL.From(user)
.Select(user.ID) //僅查詢一個屬性欄位 ID
.Where(cmp => cmp.EqualValue(user.Name) & cmp.EqualValue(user.Pwd))
.END;
User dbUser = EntityQuery<User>.QueryObject(q);
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
跟例1一樣,這裡也要求user 物件的Name和Pwd屬性必須事先有值。本例沒有使用OQL的擴充套件方法。
3.3, EntityQuery 泛型查詢方法
本例只是對例子1做了下改進,重點在於登入方法的引數不是使用者物件,而是名字和密碼引數。
/// <summary>
/// 使用使用者名稱密碼引數來登入,採用 EntityQuery 泛型查詢方法
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public bool Login2(string name, string pwd)
{
User user = new User()
{
Name = name,
Pwd = pwd
};
OQL q = OQL.From(user)
.Select(user.ID)
.Where(user.Name, user.Pwd)
.END;
User dbUser = EntityQuery<User>.QueryObject(q);
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
3.4,使用OQLConditon 物件為查詢條件
/// <summary>
/// 使用使用者名稱密碼引數來登入,使用早期的例項化OQL物件的方式,並使用OQLConditon 物件為查詢條件
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public bool Login3(string name, string pwd)
{
User user = new User();
OQL q = new OQL(user);
q.Select(user.ID).Where(q.Condition.AND(user.Name, "=", name).AND(user.Pwd, "=", pwd));
User dbUser = EntityQuery<User>.QueryObject(q);
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
這是OQL早期的條件查詢方式,缺點是沒法構造複雜的查詢條件。
3.5,操作符過載
OQLCompare 的操作符過載可以簡化比較條件,如下所示:
/// <summary>
/// 使用使用者名稱密碼引數來登入,並使用操作符過載的查詢條件比較方式
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public bool Login4(string name, string pwd)
{
User user = new User();
OQL q = OQL.From(user)
.Select()
.Where( cmp => cmp.Property(user.Name) == name
& cmp.Property(user.Pwd) == pwd )
.END;
User dbUser = EntityQuery<User>.QueryObject(q);
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
3.6,使用泛型OQL查詢(GOQL)
使用泛型OQL查詢(GOQL),對於單實體類查詢最簡單的使用方式,缺點是不能進行“連表查詢”,即多個實體類聯合查詢。
/// <summary>
/// 使用使用者名稱密碼引數來登入,使用泛型OQL查詢(GOQL),對於單實體類查詢最簡單的使用方式。
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public bool Login5(string name, string pwd)
{
User dbUser = OQL.From<User>()
.Select()
.Where((cmp, user) => cmp.Property(user.Name) == name
& cmp.Property(user.Pwd) == pwd )
.END
.ToObject();
return dbUser != null; //查詢到使用者實體類,表示登入成功
}
3.7,使用實體類主鍵來查詢
SOD實體類的“主鍵”欄位是可以修改的,這樣你可以隨時修改它,就像實體類本來的主鍵一樣,用它來填充資料,本例就是判斷是否填充成功當前實體類來判斷使用者是否可以登入。
/// <summary>
/// 使用使用者名稱密碼引數來登入,但是根據實體類的主鍵來填充實體類並判斷是否成功。
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public bool Login6(string name, string pwd)
{
User user = new User();
user.PrimaryKeys.Clear();
user.PrimaryKeys.Add("Name");
user.PrimaryKeys.Add("Pwd");
user.Name = name;
user.Pwd = pwd;
bool result= EntityQuery<User>.Fill(user);//靜態方法,使用預設的連線物件
return result;
}
3.8,查詢多條資料
前面的例子都只是查詢一條資料,如果需要查詢多條資料也很簡單,參見下面的例子,如何查詢所有姓 zhang 的使用者:
/// <summary>
/// 模糊查詢使用者,返回使用者列表,使用OQLCompare 委託
/// </summary>
/// <param name="likeName">要匹配的使用者名稱</param>
/// <returns>使用者列表</returns>
public List<User> FuzzyQueryUser(string likeName)
{
User user = new User();
OQL q = OQL.From(user)
.Select()
.Where(cmp => cmp.Comparer(user.Name, "like", likeName+"%") )
.END;
List<User> users = EntityQuery<User>.QueryList(q);
return users;
}
前端呼叫:
//查詢列表
var users=service.FuzzyQueryUser("zhang");
Console.WriteLine("模糊查詢姓 張 的使用者,數量:{0}",users.Count );
所以,查詢多條資料,僅需要使用泛型 EntityQuery物件的QueryList 方法即可。同樣,框架也為你提供了OQL物件擴充套件方法來直接查詢列表資料。
3.9,實體類聯合查詢
這裡不再舉例,我的部落格文章也多次說明過,請參考下面這個系列文章:
ORM查詢語言(OQL)簡介--高階篇(續):廬山真貌
ORM查詢語言(OQL)簡介--高階篇:脫胎換骨
ORM查詢語言(OQL)簡介--例項篇
ORM查詢語言(OQL)簡介--概念篇
四、SOD框架的設計原則
SOD框架的整個設計都採用了“條條道路通羅馬”的原則,即多模式解決方案,對資料的開發不管是SQL-MAP ,ORM,Data Control 哪種模式都可以“殊途同歸”,對於OQL,這個也得到了充分的體現,比如上面的使用者登入功能,使用了7 種方式來實現,實際上還有3中查詢方式本篇文章沒有做成說明;而對於實體類的增,刪,改,分別又提供了DbContext,OQL,泛型EntityQuery 等多種方式。
所以,SOD框架的使用非常靈活,你可以根據你的偏好,習慣,環境,來靈活使用,而且也容易擴充套件,因此,相對於EF這樣的ORM框架來,SOD框架的ORM功能沒有任何束縛,它自由,靈活,而且輕量,容易擴充套件,但不妨礙它的強大,比如對於分表分庫的查詢,資料的批量更新插入修改,資料庫鎖的直接支援等這些“企業級”資料開發需求的支援。
五、相關資源
本篇文章的原始碼,已經上傳在了框架的開源專案 pwmis.codeplex.com 網站上,可以通過下面的地址檢視完整的原始碼:
http://pwmis.codeplex.com/SourceControl/latest#SOD/Test/SampleORMTest/Program.cs
原始碼下載,可以在下面的地址檢視:
http://pwmis.codeplex.com/releases/view/117822
有關框架更詳細而完整的入門指引,請參考下面這篇文章:
PDF.NET SOD 開源框架紅包派送活動 && 新手快速入門指引
更多完整而詳細的資訊,請看框架官網地址:
框架已經完全開源,參看這篇文章:
一年之計在於春,2015開篇:PDF.NET SOD Ver 5.1完全開源
另外,網友 廣州-四糸奈 在他的"驚鴻哥的港灣"寫了一篇基礎入門介紹文章《PDF.NET最初等入門》,推薦大家看看。
網友 廣州-銀古 寫了一篇《SOD讓你的舊程式碼煥發青春》,講述瞭如何改造老式僵化專案的過程,推薦大家看看。
六、最新原始碼資源地址
CodePlex online : http://pwmis.codeplex.com/SourceControl/latest
CodePlex SVN : https://pwmis.codeplex.com/svn
GitHub : https://github.com/znlgis/PDF.NET-SOD
OSChina : http://git.oschina.net/dxzyx/SOD