Asp.Net Core Identity 騷斷腿的究極魔改實體類

来源:https://www.cnblogs.com/coredx/archive/2020/02/15/12310010.html
-Advertisement-
Play Games

前言 預設的 Identity 實體類型在大多數時候已經基本夠用,很多時候也只是稍微在 IdentityUser 類中增加一些自定義數據欄位,比如頭像。這次,我要向園友隆重介紹我魔改之後的 Identity 實體類,能支持一些特別風騷的操作。當然也完全相容內置的 UserManager、RoleMa ...


前言

       預設的 Identity 實體類型在大多數時候已經基本夠用,很多時候也只是稍微在 IdentityUser 類中增加一些自定義數據欄位,比如頭像。這次,我要向園友隆重介紹我魔改之後的 Identity 實體類,能支持一些特別風騷的操作。當然也完全相容內置的 UserManager、RoleManager 和 SignInManager,畢竟也是從內置類型繼承擴展出來的。

正文

       魔改的實體類基於一組我自定義實體介面,這組介面我也實現了一組打包好的基礎類型。因為 Identity 系列實體類型已經存在,而 C# 不支持多重繼承,所以只能把這些代碼在魔改的 Identity 實體類中粘貼幾次了。

       先來看看這些基本介面吧:

  1     /// <summary>
  2     /// 軟刪除介面
  3     /// </summary>
  4     public interface ILogicallyDeletable
  5     {
  6         /// <summary>
  7         /// 邏輯刪除標記
  8         /// </summary>
  9         bool IsDeleted { get; set; }
 10     }
 11 
 12     /// <summary>
 13     /// 活動狀態標記介面
 14     /// </summary>
 15     public interface IActiveControllable
 16     {
 17         /// <summary>
 18         /// 活動狀態標記
 19         /// </summary>
 20         bool? Active { get; set; }
 21     }
 22 
 23     /// <summary>
 24     /// 樂觀併發介面
 25     /// </summary>
 26     public interface IOptimisticConcurrencySupported
 27     {
 28         /// <summary>
 29         /// 行版本,樂觀併發鎖
 30         /// </summary>
 31         [ConcurrencyCheck]
 32         string ConcurrencyStamp { get; set; }
 33     }
 34 
 35     /// <summary>
 36     /// 插入順序記錄介面
 37     /// </summary>
 38     public interface IStorageOrderRecordable
 39     {
 40         /// <summary>
 41         /// 非自增順序欄位作為主鍵類型
 42         /// 應該在此列建立聚集索引避免隨機的欄位值導致資料庫索引性能下降
 43         /// 同時保存數據插入先後的信息
 44         /// </summary>
 45         long InsertOrder { get; set; }
 46     }
 47 
 48     /// <summary>
 49     /// 創建時間記錄介面
 50     /// </summary>
 51     public interface ICreationTimeRecordable
 52     {
 53         /// <summary>
 54         /// 實體創建時間
 55         /// </summary>
 56         DateTimeOffset CreationTime { get; set; }
 57     }
 58 
 59     /// <summary>
 60     /// 最後修改時間記錄介面
 61     /// </summary>
 62     public interface ILastModificationTimeRecordable
 63     {
 64         /// <summary>
 65         /// 最後一次修改時間
 66         /// </summary>
 67         DateTimeOffset LastModificationTime { get; set; }
 68     }
 69 
 70     /// <summary>
 71     /// 創建人id記錄介面
 72     /// </summary>
 73     /// <typeparam name="TIdentityKey">創建人主鍵類型</typeparam>
 74     public interface ICreatorRecordable<TIdentityKey>
 75         where TIdentityKey : struct, IEquatable<TIdentityKey>
 76     {
 77         /// <summary>
 78         /// 創建人Id
 79         /// </summary>
 80         TIdentityKey? CreatorId { get; set; }
 81     }
 82 
 83     /// <summary>
 84     /// 創建人記錄介面
 85     /// </summary>
 86     /// <typeparam name="TIdentityKey">創建人主鍵類型</typeparam>
 87     /// <typeparam name="TIdentityUser">創建人類型</typeparam>
 88     public interface ICreatorRecordable<TIdentityKey, TIdentityUser> : ICreatorRecordable<TIdentityKey>
 89         where TIdentityKey : struct , IEquatable<TIdentityKey>
 90         where TIdentityUser : IEntity<TIdentityKey>
 91     {
 92         /// <summary>
 93         /// 創建人
 94         /// </summary>
 95         TIdentityUser Creator { get; set; }
 96     }
 97 
 98     /// <summary>
 99     /// 上次修改人id記錄介面
100     /// </summary>
101     /// <typeparam name="TIdentityKey">上次修改人主鍵類型</typeparam>
102     public interface ILastModifierRecordable<TIdentityKey>
103         where TIdentityKey : struct, IEquatable<TIdentityKey>
104     {
105         /// <summary>
106         /// 上一次修改人Id
107         /// </summary>
108         TIdentityKey? LastModifierId { get; set; }
109     }
110 
111     /// <summary>
112     /// 上次修改人記錄介面
113     /// </summary>
114     /// <typeparam name="TIdentityKey">上次修改人主鍵類型</typeparam>
115     /// <typeparam name="TIdentityUser">上次修改人類型</typeparam>
116     public interface ILastModifierRecordable<TIdentityKey, TIdentityUser> : ILastModifierRecordable<TIdentityKey>
117         where TIdentityKey : struct, IEquatable<TIdentityKey>
118         where TIdentityUser : IEntity<TIdentityKey>
119     {
120         /// <summary>
121         /// 上一次修改人
122         /// </summary>
123         TIdentityUser LastModifier { get; set; }
124     }
View Code

       這些基本介面每一個都對應了一個基本功能。還有一個稍微複雜的樹形數據結構介面:

 1     /// <summary>
 2     /// 樹形數據介面
 3     /// </summary>
 4     /// <typeparam name="T">節點數據類型</typeparam>
 5     public interface ITree<T>
 6     {
 7         /// <summary>
 8         /// 父節點
 9         /// </summary>
10         T Parent { get; set; }
11 
12         /// <summary>
13         /// 子節點集合
14         /// </summary>
15         IList<T> Children { get; set; }
16 
17         /// <summary>
18         /// 節點深度,根的深度為0
19         /// </summary>
20         int Depth { get; }
21 
22         /// <summary>
23         /// 是否是根節點
24         /// </summary>
25         bool IsRoot { get; }
26 
27         /// <summary>
28         /// 是否是葉節點
29         /// </summary>
30         bool IsLeaf { get; }
31 
32         /// <summary>
33         /// 是否有子節點
34         /// </summary>
35         bool HasChildren { get; }
36 
37         /// <summary>
38         /// 節點路徑(UNIX路徑格式,以“/”分隔)
39         /// </summary>
40         string Path { get; }
41     }
View Code

       然後是打包介面,主要是把基本介面打包到一個統一介面,方便批量使用:

 1     /// <summary>
 2     /// 實體介面
 3     /// </summary>
 4     public interface IEntity {}
 5 
 6     /// <summary>
 7     /// 泛型實體介面,約束Id屬性
 8     /// </summary>
 9     public interface IEntity<TKey> : IEntity
10         where TKey : IEquatable<TKey>
11     {
12         TKey Id { get; set; }
13     }
14 
15     /// <summary>
16     /// 領域實體介面,主要是整合各個小介面
17     /// </summary>
18     public interface IDomainEntity : IEntity
19         , ILogicallyDeletable
20         , ICreationTimeRecordable
21         , ILastModificationTimeRecordable
22         , INotifyPropertyChanged
23         , INotifyPropertyChangedExtension
24         , IPropertyChangeTrackable
25     {}
26 
27     /// <summary>
28     /// 泛型領域實體介面
29     /// </summary>
30     public interface IDomainEntity<TKey> : IEntity<TKey>
31         , IDomainEntity
32         where TKey : struct, IEquatable<TKey>
33     {}
View Code

       樹形數據結構也有一套:

 1     /// <summary>
 2     /// 樹形實體介面
 3     /// </summary>
 4     /// <typeparam name="T">實體類型</typeparam>
 5     public interface ITreeEntity<T> : IEntity, ITree<T>
 6     {
 7     }
 8 
 9     /// <summary>
10     /// 樹形實體介面
11     /// </summary>
12     /// <typeparam name="TKey">主鍵類型</typeparam>
13     /// <typeparam name="TEntity">實體類型</typeparam>
14     public interface ITreeEntity<TKey, TEntity> : ITreeEntity<TEntity>, IEntity<TKey>
15     where TKey : IEquatable<TKey>
16     where TEntity : ITreeEntity<TKey, TEntity>
17     {
18     }
19 
20     /// <summary>
21     /// 樹形領域實體介面
22     /// </summary>
23     /// <typeparam name="T">數據類型</typeparam>
24     public interface IDomainTreeEntity<T> :
25         IDomainEntity
26         , ITreeEntity<T>
27     {
28     }
29 
30     /// <summary>
31     /// 樹形領域實體介面
32     /// </summary>
33     /// <typeparam name="TKey">主鍵類型</typeparam>
34     /// <typeparam name="TEntity">樹形實體類型</typeparam>
35     public interface IDomainTreeEntity<TKey, TEntity> :
36         IDomainTreeEntity<TEntity>
37         , IDomainEntity<TKey>
38         , ITreeEntity<TKey, TEntity>
39 
40         where TKey : struct, IEquatable<TKey>
41         where TEntity : IDomainTreeEntity<TKey, TEntity>
42     {
43         TKey? ParentId { get; set; }
44     }
View Code

       最後還有幾個特別用處的介面:

 1     /// <summary>
 2     /// 跟蹤屬性的變更
 3     /// </summary>
 4     public interface IPropertyChangeTrackable
 5     {
 6         /// <summary>
 7         /// 判斷指定的屬性或任意屬性是否被變更過
 8         /// </summary>
 9         /// <param name="names">指定要判斷的屬性名數組,如果為空(null)或空數組則表示判斷任意屬性</param>
10         /// <returns>
11         ///    <para>如果指定的<paramref name="names"/>參數有值,當只有參數中指定的屬性發生過更改則返回真(True),否則返回假(False)</para>
12         ///    <para>如果指定的<paramref name="names"/>參數為空(null)或空數組,當實體中任意屬性發生過更改則返回真(True),否則返回假(False)</para>
13         ///    </returns>
14         bool HasChanges(params string[] names);
15 
16         /// <summary>
17         /// 獲取實體中發生過變更的屬性集
18         /// </summary>
19         /// <returns>如果實體沒有屬性發生過變更,則返回空白字典,否則返回被變更過的屬性鍵值對</returns>
20         IDictionary<string, object> GetChanges();
21 
22         /// <summary>
23         /// 重置指定的屬性或任意屬性變更狀態(為未變更)
24         /// </summary>
25         /// <param name="names">指定要重置的屬性名數組,如果為空(null)或空數組則表示重置所有屬性的變更狀態(為未變更)</param>
26         void ResetPropertyChangeStatus(params string[] names);
27     }
28 
29     /// <summary>
30     /// 多對多導航實體介面
31     /// </summary>
32     /// <typeparam name="TIdentityKey">身份實體主鍵類型</typeparam>
33     /// <typeparam name="TIdentityUser">身份實體類型</typeparam>
34     public interface IManyToManyReferenceEntity<TIdentityKey, TIdentityUser> : IManyToManyReferenceEntity<TIdentityKey>
35         , ICreatorRecordable<TIdentityKey, TIdentityUser>
36         where TIdentityKey : struct, IEquatable<TIdentityKey>
37         where TIdentityUser : IEntity<TIdentityKey>
38     {
39     }
40 
41     /// <summary>
42     /// 多對多導航實體介面
43     /// </summary>
44     /// <typeparam name="TIdentityKey">身份實體主鍵類型</typeparam>
45     public interface IManyToManyReferenceEntity<TIdentityKey> : IManyToManyReferenceEntity
46         , ICreatorRecordable<TIdentityKey>
47         where TIdentityKey : struct, IEquatable<TIdentityKey>
48     {
49     }
50 
51     /// <summary>
52     /// 多對多導航實體介面
53     /// </summary>
54     public interface IManyToManyReferenceEntity : IEntity
55         , ICreationTimeRecordable
56     {
57     }
View Code

       至此,基本上用到的介面就定義好了,接下來就是魔改 Identity 實體類,這裡以 IdentityRole 為例,其他的可以到我的項目中查看,大同小異:

  1     public class ApplicationRole : ApplicationRole<int, ApplicationUser, ApplicationRole, ApplicationUserRole, ApplicationRoleClaim>
  2         , IStorageOrderRecordable
  3     {
  4         public ApplicationRole() { }
  5         public ApplicationRole(string roleName) => Name = roleName;
  6 
  7         public virtual long InsertOrder { get; set; }
  8     }
  9 
 10     public abstract class ApplicationRole<TKey, TIdentityUser, TIdentityRole, TUserRole, TRoleClaim> : IdentityRole<TKey>
 11         , IDomainTreeEntity<TKey, TIdentityRole>
 12         , IOptimisticConcurrencySupported
 13         , ICreatorRecordable<TKey, TIdentityUser>
 14         , ILastModifierRecordable<TKey, TIdentityUser>
 15         where TKey : struct, IEquatable<TKey>
 16         where TIdentityUser : IEntity<TKey>
 17         where TUserRole : ApplicationUserRole<TKey, TIdentityUser, TIdentityRole>
 18         where TRoleClaim : ApplicationRoleClaim<TKey, TIdentityUser, TIdentityRole>
 19         where TIdentityRole : ApplicationRole<TKey, TIdentityUser, TIdentityRole, TUserRole, TRoleClaim>
 20     {
 21         #region 重寫基類屬性使屬性變更通知事件生效
 22 
 23         public override TKey Id { get => base.Id; set => base.Id = value; }
 24         public override string ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; }
 25         public override string Name { get => base.Name; set => base.Name = value; }
 26         public override string NormalizedName { get => base.NormalizedName; set => base.NormalizedName = value; }
 27 
 28         #endregion
 29 
 30         public string Description { get; set; }
 31 
 32         /// <summary>
 33         /// 需要使用.Include(r => r.UserRoles).ThenInclude(ur => ur.Role)預載入或啟用延遲載入
 34         /// </summary>
 35         [NotMapped]
 36         public virtual IEnumerable<TIdentityUser> Users => UserRoles?.Select(ur => ur.User);
 37 
 38         #region 導航屬性
 39 
 40         public virtual List<TUserRole> UserRoles { get; set; } = new List<TUserRole>();
 41 
 42         public virtual List<TRoleClaim> RoleClaims { get; set; } = new List<TRoleClaim>();
 43 
 44         #endregion
 45 
 46         #region IDomainTreeEntity成員
 47 
 48         public virtual TKey? ParentId { get; set; }
 49 
 50         #endregion
 51 
 52         #region IEntity成員
 53 
 54         public virtual bool? Active { get; set; } = true;
 55         public virtual bool IsDeleted { get; set; }
 56         public virtual DateTimeOffset CreationTime { get; set; } = DateTimeOffset.Now;
 57         public virtual DateTimeOffset LastModificationTime { get; set; } = DateTimeOffset.Now;
 58 
 59         #endregion
 60 
 61         #region IDomainEntity成員
 62 
 63         public virtual TKey? CreatorId { get; set; }
 64         public virtual TIdentityUser Creator { get; set; }
 65         public virtual TKey? LastModifierId { get; set; }
 66         public virtual TIdentityUser LastModifier { get; set; }
 67 
 68         #endregion
 69 
 70         #region ITree成員
 71 
 72         public virtual TIdentityRole Parent { get; set; }
 73 
 74         public virtual IList<TIdentityRole> Children { get; set; }
 75 
 76         [DoNotNotify, NotMapped]
 77         public virtual int Depth => Parent?.Depth + 1 ?? 0;
 78 
 79         [DoNotNotify, NotMapped]
 80         public virtual bool IsRoot => Parent == null;
 81 
 82         [DoNotNotify, NotMapped]
 83         public virtual bool IsLeaf => Children?.Count == 0;
 84 
 85         [DoNotNotify, NotMapped]
 86         public virtual bool HasChildren => !IsLeaf;
 87 
 88         [DoNotNotify, NotMapped]
 89         public virtual string Path => Parent == null ? Id.ToString() : $@"{Parent.Path}/{Id}";
 90 
 91         #endregion
 92 
 93         #region IPropertyChangeTrackable成員
 94 
 95         private static readonly object Locker = new object();
 96         private static readonly Dictionary<Type, string[]> PropertyNamesDictionary = new Dictionary<Type, string[]>();
 97 
 98         private readonly BitArray _propertyChangeMask;
 99 
100         /// <summary>
101         /// 全局屬性變更通知事件處理器
102         /// </summary>
103         public static PropertyChangedEventHandler PublicPropertyChangedEventHandler { get; set; }
104 
105         /// <summary>
106         /// 初始化用於跟蹤屬性變更所需的屬性信息
	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這兩天複習了下HTML和CSS的一些基本內容並實現了兩個小的案例,在此整理一下。 主要內容 1. web概念概述 2. HTML 3. CSS web概念概述 JavaWeb: 使用Java語言開發基於互聯網的項目 軟體架構: 1. C/S: Client/Server 客戶端/伺服器端 在用戶本地 ...
  • 瀏覽器訪問網站過程 1. 用戶在瀏覽器地址欄中輸入網址 2. 瀏覽器解析網址構建HTTP請求 HTTP請求報文包括:請求行、請求頭和請求體 3. 瀏覽器發起DNS解析請求,將功能變數名稱轉化為IP地址 網址映射到伺服器IP地址,指定了訪問的伺服器 4. 瀏覽器發送請求報文給到伺服器 5. 伺服器接收並解析報 ...
  • 佈局是什麼?根據功能劃分小塊,再根據設計稿還原,書寫靜態頁面,然後再在塊裡面填充內容,完善功能,js施加交互效果。div作為一個容器,獨占一行,代碼書寫習慣從上至下屬於標準流,而浮動float的css樣式則打破div(標準流)獨占一行的傳統!具體代碼展示如下: ...
  • 盒模型規定了元素框處理元素內容width與height值、內邊距padding、邊框border 和 外邊距margin 的數值大小。邊框內的空白是內邊距padding,邊框外的空白是外邊距margin,如下所示,這個盒模型元素框的寬度值=內容區域的寬度+2(內邊距+外邊距+邊框),也就是該示例中的... ...
  • 能找到一份前端開發工作,首先你起碼得是一個合格的初級前端工程師。那麼,什麼是初級前端工程師?初級前端工程師都會做些什麼?這個問題需要分為以下幾個方面來說: ...
  • 前言 現在的前端門檻越來越高,不再是只會寫寫頁面那麼簡單。模塊化、自動化、跨端開發等逐漸成為要求,但是這些都需要建立在我們牢固的基礎之上。不管框架和模式怎麼變,把基礎原理打牢才能快速適應市場的變化。下麵介紹一些常用的源碼實現: call實現 bind實現 new實現 instanceof實現 Obj ...
  • 前言 昨天一番發了一篇批量下載手機壁紙的文章,分享了抓取到的美圖給小伙伴,然後一番就美美的去碎覺了。 早上起來看到有小伙伴在日更群里說有沒有狗哥的?憨憨的一番以為就是狗的圖片,於是就發了幾張昨天抓取的狗的圖片。 在群友的幫助下,一番才知道是愛情公寓里的一個演員。 小伙伴有需求,一番本著力所能及的幫助 ...
  • 時間輪演算法 時間輪是一種高效、低延遲的調度數據結構。其在Linux內核中廣泛使用,是Linux內核定時器的實現方法和基礎之一。按使用場景,大致可以分為兩種時間輪:原始時間輪和分層時間輪。分層時間輪是原始時間輪的升級版本,來應對時間“槽”數量比較大的情況,對記憶體和精度都有很高要求的情況。延遲任務的場景 ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...