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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...