快速傳送
手擼ORM淺談ORM框架之Update篇
手擼ORM淺談ORM框架之Delete篇
手擼ORM淺談ORM框架之Query篇
後續待定。。。。。。
合抱之木,生於毫末
反射
在電腦科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行為的描述(self-representation)和監測(examination),並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
手擼ORM淺談ORM框架系列使用反射機制來實現的動態獲取所需要的資訊,反射機制有優點也有缺點,使用了反射我們可以不用每次更新實體模型資料訪問層轉換Sql等很多需要修改的地方,享受快捷便利的同時肯定會帶來相應的缺點,反射的效能在執行同樣的操作時會略低於直接使用強型別語言的原生特性,畢竟它至少多了動態獲取資訊步驟;我們不能因此否認反射,反射確實在很多場景可以為我們做很多不必要的重複性工作,可以節約出時間做更棘手的問題。
對於我們需要使用什麼決定因素需要看使用前後的變化,利大於弊且符合業務需要就放心使用,完美不存在,更美從未止步。。。
泛型
泛型(摘錄百度百科:泛型)
由於.NET Framework 泛型的型別引數之實際型別在執行時均不會被消除,執行速度會因為型別轉換的次數減少而加快。Java 泛型的引數只可以代表類,不能代表個別物件。由於 Java 泛型的型別引數之實際型別在編譯時會被消除,所以無法在執行時得知其型別引數的型別。Java 編譯器在編譯泛型時會自動加入型別轉換的編碼,故執行速度不會因為使用泛型而加快。
九層之臺,起於累土
主要通過反射獲取實體資訊,目前專案中的實體唯一主鍵統一使用的bigint自動遞增。
BaseRepository-》GetCurrentTableName獲取表名稱;
1 /// <summary> 2 /// get current-table-name 3 /// </summary> 4 /// <returns>return table-name</returns> 5 private string GetCurrentTableName() 6 { 7 string currentTableName = string.Empty; 8 var t = typeof(T); 9 if (t != null && !t.Name.IsNullOrEmpty()) 10 { 11 currentTableName = t.Name; 12 } 13 if (currentTableName.IsNullOrEmpty()) 14 { 15 throw new ArgumentNullException("get table-name is null"); 16 } 17 return currentTableName; 18 }
BaseRepository-》GetExcludeKeyAllFields獲取主鍵以外的public欄位;
1 /// <summary> 2 /// get exclude key all fields 3 /// </summary> 4 /// <returns>return exclude key all fields</returns> 5 private List<PropertyInfo> GetExcludeKeyAllFields() 6 { 7 PropertyInfo[] properties = typeof(T).GetProperties(); 8 // filter abstract virtual property 9 properties = properties.Where(p => p.PropertyType.IsAbstract == false && !p.GetMethod.IsVirtual == true).ToArray(); 10 if (properties == null || properties.Length <= 0) 11 { 12 throw new ArgumentNullException("public value fields is null"); 13 } 14 List<PropertyInfo> list = new List<PropertyInfo>(); 15 foreach (var item in properties) 16 { 17 if (item.CustomAttributes.Any(c => c.AttributeType.Name == nameof(KeyAttribute))) 18 { 19 continue; 20 } 21 list.Add(item); 22 } 23 if (list == null || list.Count <= 0) 24 { 25 throw new ArgumentNullException("public value fields is null"); 26 } 27 return list; 28 }
BaseRepository-》GetValue獲取欄位對應的值,String和Char前面分別加了N字首(Sql Server反射弧),MySql中文字元可加或不加中文欄位不會出現亂碼,若出現亂碼可以根據實際情況設定MySql資料庫字符集的格式;
1 /// <summary> 2 /// get value 3 /// </summary> 4 /// <param name="property"></param> 5 /// <param name="entity"></param> 6 /// <returns></returns> 7 private string GetValue(PropertyInfo property , T entity) 8 { 9 var val = property.GetValue(entity); 10 if (val == null) 11 { 12 return "NULL"; 13 } 14 else 15 { 16 string prefixN = string.Empty; 17 if (property.PropertyType.Name == nameof(String) || property.PropertyType.Name == nameof(Char)) 18 { 19 prefixN = "N"; 20 } 21 if (property.PropertyType.Name == nameof(Boolean)) 22 { 23 return string.Format("{0}", GetBoolValue(val)); 24 } 25 return string.Format("{0}'{1}'", prefixN, val); 26 } 27 }
BaseRepository-》GetBoolValue值屬性轉換,Sql Server中資料型別bit:'true' or 'false' 等同於'1' or '0';MySql 8.x資料型別bit: 需要把bool 'true' or 'false' 轉換成'1' or '0' 。目前專案中使用到的資料型別bigint、varchar、int、char、datetime、bit,bit->需要轉換;
1 /// <summary> 2 /// get bool value 3 /// mysql true or false convert 1 or 0 4 /// </summary> 5 /// <param name="obj"></param> 6 /// <returns></returns> 7 private int GetBoolValue(object obj) 8 { 9 //return obj.ToString().ToLower() == "false" ? 0 : 1; 10 if (obj.ToString().ToLower() == "false") 11 { 12 return 0; 13 } 14 return 1; 15 }
BaseRepository-》GetInsertSql一路走來Sql出現了(提高效能可以優化,快取當前專案所有表的增刪查改Sql語句)
1 /// <summary> 2 /// get insert sql 3 /// </summary> 4 /// <param name="entity">entity</param> 5 /// <returns>return insert sql</returns> 6 private string GetInsertSql(T entity) 7 { 8 string tableName = GetCurrentTableName(); 9 StringBuilder sbField = new StringBuilder(); 10 StringBuilder sbValue = new StringBuilder(); 11 PropertyInfo[] properties = GetAllFields(true); 12 foreach (var item in properties) 13 { 14 sbField.AppendFormat("{0},", item.Name); 15 sbValue.AppendFormat("{0},", GetValue(item, entity)); 16 } 17 sbField.Remove(sbField.Length - 1, 1); 18 sbValue.Remove(sbValue.Length - 1, 1); 19 //todo ;SELECT @@identity return identity 20 return string.Format("INSERT INTO {0} ({1}) VALUES ({2})", tableName, sbField, sbValue); 21 }
BaseRepository-》Add千呼萬喚始出來,終於到寫入資料庫了(Add成功後沒有返回實體因為NET Core的DbContext取消了SqlQuery,基類BaseRepository實現Add後返回當前的實體主鍵,基本原生 SQL 查詢 可使用 FromSqlRaw 擴充套件方法基於原始 SQL 查詢開始 LINQ 查詢。 FromSqlRaw 只能在直接位於 DbSet<> 上的查詢根上使用;NET Framework DBContext中有此方法SqlQuery,可以使用Query在Insert語句後面;SELECT @@identity返回int、bigint型別自動遞增主鍵並賦值給當前實體,如果是指定主鍵直接返回當前實體,也期待園友提出更好的解決方案)
1 /// <summary> 2 /// add entity 3 /// </summary> 4 /// <param name="entity">entity</param> 5 /// <returns>return true or false</returns> 6 public bool Add(T entity) 7 { 8 string sql = GetInsertSql(entity); 9 context.Database.ExecuteSqlRaw(sql); 10 return true; 11 }
實操Repository方法泛型約束
1 /// <summary> 2 /// LearnStudentRepository 3 /// </summary> 4 public partial class LearnStudentRepository: ILearnStudentRepository 5 { 6 public bool Add(Learn_Student learnStudent) 7 { 8 using (MySqlDbContext mySqlDbContext=new MySqlDbContext()) 9 { 10 BaseRepository<Learn_Student> baseRepository = new BaseRepository<Learn_Student>(mySqlDbContext); 11 return baseRepository.Add(learnStudent); 12 } 13 } 14 }
登高望遠,更上層樓
learn-orm-net缺少以下情況的處理:
- 1. Sql語句沒有快取,快取提高一些效能;
- 2.不支援指定主鍵的實體,既是已經提前把主鍵生成好了;
- 3.專案使用的實體是自動遞增主鍵並且沒有返回主鍵的值,不能滿足主子表有外來鍵關係業務場景,既是子表存主表的主鍵(自動遞增型別主鍵);
- 4.複合主鍵(主鍵都是指定主鍵,提前按照一定規則生成的鍵值);
- 5.複合主鍵裡面包含自增主鍵,既是有指定型別主鍵也有自動遞增型別主鍵;
- 6.實體包含導航屬性級聯寫入資料庫(類似於3)
- (如果業務需要特殊的方式,ORM框架沒有我們可以寫基類方法或者擴充套件方法來適應專案需要,待補充...)
- 注:learn-orm-net目前只是作為學習ORM框架原理的Demo,專案會做出一定的優化處理,但不能直接拿來在專案中使用,畢竟現在NET Framework、NET Core已經有很多優秀的ORM框架,NET下一次釋出就是隻有一個版本了,我們沒有必要重複造輪子,造輪子是因為沒有現成的優秀的輪子可用。
如果只是停留在會使用當前專案所使用的ORM框架基本增刪查改,對於根據業務更好的使用ORM框架是有點困難的;所以,深入理解ORM原理,為未來的某個時刻我們遇到了問題,更好的根據ORM框架有的功能做出比較符合業務需要的程式;或者,擴充套件當前ORM框架沒有的功能來適應我們專案的業務需求。
程式碼下載地址: SourceCode 作者水平有限歡迎園友糾正錯誤及不恰當之處,予以及時修正以免誤導他人!