經過一番深思熟慮,決定先用Dapper吧.....
以下是我感覺比較有用的一些東西
Dapper項目地址 https://github.com/StackExchange/dapper-dot-net
初次接觸Dapper,簡單的范例 https://github.com/xliang/dapper-net-sample
園子里樹上的蝸牛 大大寫的擴展系列 http://www.cnblogs.com/cyb331/p/3514555.html 文中的下載連接都失效了,這是擴展1.1的下載連接 http://pan.baidu.com/s/1bngXOAz
如果以前寫過sql語句,Dapper用起來算是比較順手的,基本上還是以前那套 數據庫連接,sql語句,記錄集 的流程,只不過返回結果的時候就不用去記錄集一個字段一個字段的讀了了,結果可以直接綁定到模型上....
Dapper項目自帶了Contrib 和 Rainbow兩個插件,我還沒深入去了解,不知道還有沒有什么高級功能,暫且當成語法糖吧,隨着學習的深入,可能會有更深的理解
從Dapper.net sample 可以看到直接用Dapper和兩個插件完成增刪改查的區別,如果你也是新手,沒接觸過,建議把這個范例下回去看看,基本大概就明白了
我這里就簡單的把插入數據的代碼拿出來,簡單做個比較
Dapper直接插入一條數據是這樣的
var supplier = new Supplier() { Address = "10 Main Street", CompanyName = "DEF Corporation" }; sqlConnection.Execute( @" insert Suppliers(CompanyName, Address) values (@CompanyName, @Address) ", supplier);
Contrib插件是這樣的
var supplier = new Supplier() { Address = "10 Main Street", CompanyName = "ABC Corporation" }; var supplierId = sqlConnection.Insert<Supplier>(supplier);
Rainbow是這樣的
int? supplierId = db.Suppliers.Insert(new { CompanyName = Guid.NewGuid().ToString() });
感覺上Rainbow在用的時候跟EF的流程差不多,定義Database的時候要把表名和實體(模型)都定義好...感覺這樣用就不如直接去用EF了....所以,我的代碼里面大都用了Contrib來做
public class NorthwindDatabase : Database<NorthwindDatabase> { public Table<Supplier> Suppliers { get; set; } }
另外還要介紹就是 樹上的蝸牛 寫的擴展,看了一下介紹和擴展的內容,感覺以后有些地方可能會用的上,於是也都導入到解決方案里面了...
這里要說明一下的是,蝸牛大大寫的DapperEx是從DbBase擴展的,而Contrib是從DbConnecttion擴展的....
為了優化性能,實現一個請求使用一個數據庫連接,所以我寫了一個DBFactory,用CallContext來存儲數據庫連接實例,有關CallContext的說明,請msdn或者google,做為初學者,暫時只知道這樣做可以就行了...
代碼如下

public static class DBFactory { /// <summary> /// 獲取主數據庫連接 /// </summary> /// <returns></returns> public static DbBase GetSDDB() { DbBase db = CallContext.GetData("SDDB") as DbBase; if (db == null) { db = new DbBase("SdConnection"); CallContext.SetData("SDDB", db); } return db; } }
需要數據庫的時候這樣寫就可以了
public class ServiceBase { protected DbBase DB; public ServiceBase() { //取數據庫對象 DB = DBFactory.GetSDDB(); } }
具體操作的時候這樣就OK了
public IEnumerable<StaffUser> GetUserAll(int page = 0, int rows = 0) { IEnumerable<StaffUser> m = DB.DbConnecttion.Query<StaffUser>(@" Select * From StaffUser order by IsEnable Desc,ID" + SDUtility.GetPageSql(page, rows)); return m; }
Contrib插件有一個地方很別扭,就是在增刪改查的時候,會把Model后面加個s當作表名,這個行為也是學EF,不過人家EF更智能的一點是,會自動把英文單詞轉換為正確的復數形式,而Contrib就是簡單粗暴的在后面加了個s,讓我感覺很是不爽,於是乎就粗暴的打開源代碼,找到 GetTableName 函數,改之......這樣我model什么名字跟數據庫的表名相同就OK了....
當時打算用蝸牛大大寫的DapperEx,還有一個原因就是原生的dapper不提供翻頁支持....這個也讓人很是惱火,以前用mssql的時候最惱火的也是這個....不如用mysql,直接limit xx,yy 就行了....
不過后來偶然查資料得知,mssql2012的ORDER BY 語句支持用 offset_fetch 來實現翻頁(控制返回的行數) ,微軟的msdn里有代碼示例 http://msdn.microsoft.com/zh-cn/library/ms188385(v=sql.110).aspx#Offset
所以 SDUtility.GetPageSql 里的代碼就簡單的許多,直接拼接出 offset_fetch 語句即可
public static string GetPageSql(int page = 0, int rows = 0) { page = page - 1; if (page < 0) page = 0; if (rows < 0) rows = 0; if (page==0 && rows==0) return ""; return string.Format(" OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY",page*rows,rows); }
有了dapper,Contrib還有offset_fetch,再加上高大上sql語句,哦,對了,還有功不可沒的Json.net,平時大部分的需求都可以滿足了.....
復雜的表關系查詢直接一句sql搞定,再也不像EF那樣絞盡腦汁的去設計實體了....而且用dynimic和Json.net返回數據,也可以省下很多model,想想還真是有點小雞凍,吼吼~~~
再就是還有一個可能在實際使用中可能遇到的問題...就是更新和查詢的時候,我們有可能只需要model的其中幾個屬性(字段),我是這樣解決的....
在model里面加了個方法,根據不同的操作需要,返回需要的字段,比如,在更新用戶信息的時候,,有些字段是不能動的特別用來加密密碼的salt字段,一旦生成了就不能動了...
所以我的model會這樣寫,由於剛接觸C#,完全面向對象的思想還沒有深入,我也不知道這種寫法會不會違反什么編程原則,總之,先湊合能用就行....如果你有更好的方法,也歡迎交流學習......
GetUpdatePart用戶返回編輯用戶時需要更新的字段(登錄次數,最后登錄時間,還有salt字段要避開)
PrepareToUpdate 是在更新數據之前調用一下,傳回原來數據庫中的數據做對比,看看密碼是否更改了,如果密碼改了,就根據salt重新加密一下
PrepareToInsert 是在插入新數據之前調用,先生成一個salt,然后再對密碼進行加密,同時把登錄次數,最后登錄時間賦值一下,防止出現null
public class StaffUser { [Key] //For Contrib [Id(CheckAutoId = true)]//for Ex public int ID { get; set; } public string UserCode { get; set; } public string UserName { get; set; } public string Nick { get; set; } public string Password { get; set; } public byte IsEnable {get; set; } public int LoginCount { get; set; } public DateTime LastLogin { get; set; } public string Description { get; set; } public string salt { get; set; } //只更新部分字段 public string[] GetUpdatePart() { return new string[] { "UserCode", "UserName", "Nick", "Password", "IsEnable", "Description" }; } //更新前准備 public string PrepareToUpdate(StaffUser Old) { //檢查密碼是否被修改 if (Old.Password!=this.Password) { //重新生成密碼 this.Password = SDUtility.MD5(SDUtility.MD5(this.Password) + Old.salt); } return ""; } //添加前准備 public string PrepareToInsert() { //生成鹽和密碼 this.salt = SDUtility.GetSalt(); this.Password = SDUtility.MD5(SDUtility.MD5(this.Password) + this.salt); LoginCount = 0; LastLogin = DateTime.Now; return ""; } }
這樣,我在插入新數據的時候就可以用 Contrib 了
private string DoUserInsert(StaffUser row) { try { //為插入做准備 row.PrepareToInsert(); DB.DbConnecttion.Insert<StaffUser>(row); return ""; } catch (Exception ex) { return row.ID + row.UserName + "插入失敗! " + ex.Message; } }
為了用Contrib在update的時候實現只更新部分字段,所以繼續對 SqlMapperExtensions 進行改造,增加了一個 UpdatePart 方法,就是把原來的Update方法里更新所有字段改成只更新參數傳入的字段

public static bool UpdatePart<T>(this IDbConnection connection, T entityToUpdate,string[] UpdateProps , IDbTransaction transaction = null, int? commandTimeout = null) where T : class { var proxy = entityToUpdate as IProxy; if (proxy != null) { if (!proxy.IsDirty) return false; } var type = typeof(T); var keyProperties = KeyPropertiesCache(type); if (!keyProperties.Any()) throw new ArgumentException("Entity must have at least one [Key] property"); var name = GetTableName(type); var sb = new StringBuilder(); sb.AppendFormat("update {0} set ", name); var allProperties = TypePropertiesCache(type); var computedProperties = ComputedPropertiesCache(type); //var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties)); //只更新指定字段 for (var i = 0; i < UpdateProps.Length; i++) { var property = UpdateProps[i]; sb.AppendFormat("{0} = @{1}", property, property); if (i < UpdateProps.Length - 1) sb.AppendFormat(", "); } sb.Append(" where "); for (var i = 0; i < keyProperties.Count(); i++) { var property = keyProperties.ElementAt(i); sb.AppendFormat("{0} = @{1}", property.Name, property.Name); if (i < keyProperties.Count() - 1) sb.AppendFormat(" and "); } var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction); return updated > 0; }
然后更新數據的時候這樣使用...
private string DoUserUpdate(StaffUser row) { try { //取出原來數據 var old = DB.DbConnecttion.Get<StaffUser>(row.ID); //為更新做准備 row.PrepareToUpdate(old); //只更新部分字段 DB.DbConnecttion.UpdatePart<StaffUser>(row,row.GetUpdatePart()); return ""; } catch (Exception ex) { return row.ID + row.UserName + "更新失敗! " + ex.Message; } }