我們在設計資料庫表的時候,往往為了方便,主鍵ID一般採用字元串類型或者GUID類型,這樣對於資料庫表記錄的遷移非常方便,而且有時候可以在處理關聯記錄的時候,提前對應的ID值。但有時候進行數據記錄插入的時候,往往忽略了對ID的賦值處理。為了便於使用或者允許自動賦值,我們可以在數據訪問基類中對GUID主... ...
我們在設計資料庫表的時候,往往為了方便,主鍵ID一般採用字元串類型或者GUID類型,這樣對於資料庫表記錄的遷移非常方便,而且有時候可以在處理關聯記錄的時候,提前對應的ID值。但有時候進行數據記錄插入的時候,往往忽略了對ID的賦值處理。為了便於使用或者允許自動賦值,我們可以在數據訪問基類中對GUID主鍵進行自動賦值處理。
1、實體類主鍵屬性的處理
在我們設計基於SqlSugar的框架的時候,實體類定義一個基類Entity<T>,如下代碼所示。
[Serializable] public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey> { /// <summary> /// 實體類唯一主鍵 /// </summary> [SqlSugar.SugarColumn(IsPrimaryKey = true, ColumnDescription = "主鍵")] public virtual TPrimaryKey Id { get; set; }
一般可以擴展字元串,整形等等類型的實體類。
預設的Entity定義為整形的,如下所示。自增長的整形主鍵,不需要插入值,它在記錄寫入的時候獲得對應的Id值。
[Serializable] public abstract class Entity : Entity<int>, IEntity { /// <summary> /// ID 主鍵,自增長類型 /// </summary> [SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public override int Id { get; set; } }
對於字元型類型的ID鍵,可以在構造函數中對ID進行初始化。
/// <summary> /// 客戶信息 /// 繼承自Entity,擁有Id主鍵屬性 /// </summary> [SugarTable("T_Customer")] public class CustomerInfo : Entity<string> { /// <summary> /// 預設構造函數(需要初始化屬性的在此處理) /// </summary> public CustomerInfo() { this.Id = System.Guid.NewGuid().ToString(); this.CreateTime = System.DateTime.Now; }
或者我們在數據插入一條新記錄的時候,判斷主鍵是否為空,然後賦值給它,或者唯一的GUID值。
使用Guid.NewGuid() 的處理,這樣好處就是可以獲得一個唯一的GUID值,而弊端是ID是無序的,沒有先後順序,對ID排序就是無意義了。
為瞭解決這個問題,我們還是引入Abp VNext的規則,生成一個有序的GUID值,同時在資料庫訪問基類,對插入記錄、更新記錄的時候,判斷ID(對GUID類型或者字元串類型的主鍵ID)是否為空,為空則賦值一個有序的GUID給它,則可以完美解決問題了。
這樣我們定義實體類的時候,ID值可以不初始化,讓它保留位空,可以讓用戶主動設置值或者自動基類處理賦值。
/// <summary> /// 客戶信息 /// 繼承自Entity,擁有Id主鍵屬性 /// </summary> [SugarTable("T_Customer")] public class CustomerInfo : Entity<string> { /// <summary> /// 預設構造函數(需要初始化屬性的在此處理) /// </summary> public CustomerInfo() { this.CreateTime = System.DateTime.Now; }
2、基類判斷ID是否為空並賦值
對於GUID或者字元串類型的ID值,為什麼設置有序GUID,可以參考鏈接瞭解下:https://github.com/abpframework/abp/blob/48c52625f4c4df007f04d5ac6368b07411aa7521/docs/zh-Hans/Guid-Generation.md
一般情況下,我們利用SqlSugar插入一個新記錄的時候,是如下代碼
/// <summary> /// 創建對象 /// </summary> /// <param name="input">實體對象</param> /// <returns></returns> public virtual async Task<bool> InsertAsync(TEntity input) { return await EntityDb.InsertAsync(input); }
而為了判斷Id是否為空,我們需要對ID類型進行判斷,判斷是否字元串類型或者GUID類型,如果為空則自動賦值它,因此我們在插入前進行一個判斷處理,如下代碼所示。
/// <summary> /// 創建對象 /// </summary> /// <param name="input">實體對象</param> /// <returns></returns> public virtual async Task<bool> InsertAsync(TEntity input) { SetIdForGuids(input);//如果Id為空,設置有序的GUID值 return await EntityDb.InsertAsync(input); }
其中SetIdForGuids是獲得有序GUID的值的函數。
/// <summary> /// 為新創建的實體對象,設置主鍵Id的值為有序的GUID值(GUID類型或者字元串類型試用) /// </summary> public virtual void SetIdForGuids(TEntity entity) { if (entity is IEntity<Guid> entityWithGuidId && entityWithGuidId.Id == Guid.Empty) { //預設的GUID類型 var guidType = SequentialGuidType.SequentialAsString; switch(this.dbContext.DbType) //根據不同的資料庫類型獲取合適的生成序列方式 { case SqlSugar.DbType.SqlServer: guidType = SequentialGuidType.SequentialAtEnd; break; case SqlSugar.DbType.MySql: case SqlSugar.DbType.PostgreSQL: guidType = SequentialGuidType.SequentialAsString; break; case SqlSugar.DbType.Oracle: guidType = SequentialGuidType.SequentialAsBinary; break; } var guid = GetSequentialGuid(guidType); entityWithGuidId.Id = guid; } else if (entity is IEntity<string> entityWithStringId && string.IsNullOrWhiteSpace(entityWithStringId.Id)) { var guid = GetSequentialGuid(SequentialGuidType.SequentialAsString); entityWithStringId.Id = guid.ToString(); } }
根據不同的資料庫特性類型,構建不同的GUID值,如果是字元串的Id,我們統一採用 SequentialAsString 這個方式,這個也是支持字元串的常規排序處理,這樣我們既獲得了一個不重覆的GUID值,也可以對ID進行排序,它是根據先後順序排序的。
/// <summary> /// 獲取可以生成連續的GUID /// </summary> /// <returns></returns> protected Guid GetSequentialGuid(SequentialGuidType sequentialGuidType) {//使用指定序列創建的(生成連續的GUID) //參考鏈接瞭解細節:(https://github.com/abpframework/abp/blob/48c52625f4c4df007f04d5ac6368b07411aa7521/docs/zh-Hans/Guid-Generation.md) var options = new AbpSequentialGuidGeneratorOptions() { DefaultSequentialGuidType = sequentialGuidType //SequentialAtEnd(default) 用於SQL Server. //SequentialAsString 用於MySQL和PostgreSQL. //SequentialAsBinary 用於Oracle. }; return new SequentialGuidGenerator(options).Create(); }
添加幾個字典類型(字元串ID)的記錄進行測試。
可以看到ID的類型首碼部分是一樣的,後面變化,以ID正序排序,是根據寫入時間順序處理的。
系列文章:
《基於SqlSugar的開發框架的循序漸進介紹(1)--框架基礎類的設計和使用》
《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》
《基於SqlSugar的開發框架循序漸進介紹(3)-- 實現代碼生成工具Database2Sharp的整合開發》
《基於SqlSugar的開發框架循序漸進介紹(4)-- 在數據訪問基類中對GUID主鍵進行自動賦值處理 》
主要研究技術:代碼生成工具、會員管理系統、客戶關係管理軟體、病人資料管理軟體、Visio二次開發、酒店管理系統、倉庫管理系統等共用軟體開發
專註於Winform開發框架/混合式開發框架、Web開發框架、Bootstrap開發框架、微信門戶開發框架的研究及應用。
轉載請註明出處:
撰寫人:伍華聰 http://www.iqidi.com