在 EF Core 7 中實現強類型 ID

本文主要介紹 DDD 中的強類型 ID 的概念,及其在 EF 7 中的實現,以及使用 LessCode.EFCore.StronglyTypedId 這種更簡易的上手方式。

背景

在楊中科老師 B 站的.Net Core 視頻教程[1]其中 DDD 部分講到了強類型 ID(Strongly-typed-id)的概念,也叫受保護的密鑰(guarded keys)當時在 .NET 中的 DDD 實現是個懸而未決的問題,之后我也一直在尋找相關的實現方案。

非常高興 .NET 7 的更新帶來的 EF Core 7.0 的新增功能中,就包含了改進的值生成[2]這一部分,在自動生成關鍵屬性的值方面進行了兩項重大改進。

下面我們通過幾個例子來了解這部分的內容,以及如何更簡便的實現強類型。

強類型 ID

強類型 ID(Strongly-typed-id),又稱之為受保護的鍵(guarded keys),它是領域驅動設計(DDD) 中的一項不可或缺的功能。

簡單的來說,就是比如兩個實體都是 int、long 或是 Guid 等類型的鍵值 ID,那么這就意味著它們 ID 就有可能在編碼時被我們分配錯誤。再者一個函數如果同時傳這兩個 ID 作為參數,順序傳入錯誤,就意味著執行的結果出現問題。

在 DDD 的概念中,可以將實體的 ID 包裝到另一種特定的類型中來避免。比如將 User 的 int 型 Id 包裝為 UserId 類型,只用來它來表示 User 實體的 Id:

// 包裝前
public class User
{public int Id { get; set; }
}// 以下是包裝后
public class User
{public UserId Id { get; set; }
}

其優點非常明顯:

?代碼自解釋,不需要多余的注釋就可以看明白,提高程序的可讀性?利用編譯器提前避免不經意的編碼錯誤,提高程序的安全性

當然上面的代碼并不是具體實現的全部,需要其他更多的額外編碼工作。也就是說其增加了代碼的復雜性。DDD 中更多的是規范性設計,是為了預防缺陷的發生,讓代碼也變的更易懂了。具體是否要使用某一條規范,我們可以根據項目的具體情況進行權衡。

缺陷也總會有解決方案,集體的智慧是無窮,已經有很多技術大牛提供了更簡便的方案,我們只需要站在巨人的肩膀上體驗強類型 ID 帶來的優點和便捷就可以了,文章也會介紹如何更簡易的實現。

EF 中的使用演示

我們首次創建一個未使用強類型 ID 的 Demo,之后用不同方法實現強類型 ID 進行比較。項目都選擇 .NET 7,數據庫這里使用的是 MySql 。MySQL 中對 EF Core 7.0 的支持需要用到組件?Pomelo.EntityFrameworkCore.MySql?,當前需要其 alpha 版本。

1. 未使用強類型 ID

創建一個用于生成作者表的?Author?實體:

internal class Author
{public long Id { get; set; }public string Name { get; set; }public string Description { get; set; }
}

接下來創建一個用于生成圖書表的?Book?實體:

internal class Book
{public Guid Id { get; set; }public string BookName { get; set; }public Author? Author { get; set; }public long AuthorId { get; set; }
}

然后創建對應的?DbContext

internal class TestDbContext : DbContext
{public DbSet<Book> Books { get; set; }public DbSet<Author> Authors { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=localhost;database=test;uid=root;pwd=root;";var serverVersion = new MySqlServerVersion(new Version(8, 0, 27));optionsBuilder.UseMySql(connStr, serverVersion);optionsBuilder.LogTo(Console.WriteLine);}}

進行數據庫遷移,我們可以發現其創建的數據庫表情況如下:

58a7962688de275579f15eeac79f461b.png
數據庫

然后在?Program.cs?中編寫下列測試添加和查詢的代碼:

using ordinary;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;TestDbContext ctx = new TestDbContext();var zack = new Author
{Name = "zack",Description = "mvp"
};ctx.Authors.Add(zack);ctx.SaveChanges();ctx.Books.Add(new Book {Author= zack,BookName = "ddd .net",
});ctx.SaveChanges();var list1 = ctx.Authors.ToArray();
var list2 = ctx.Books.ToArray();Console.WriteLine("\n\n--------------------- Author Table Info  -------------------------");Console.WriteLine(JsonSerializer.Serialize(list1));Console.WriteLine("\n\n--------------------- Book Table Info  -------------------------");Console.WriteLine(JsonSerializer.Serialize(list2));

其執行結果如下:

a02a6d81af6e3318ed6468c019f5cb2e.png
執行結果

2. 基礎實現

接下來我們按照官網的說明對以上的代碼進行改造,實現基本的強類型 ID。

我們按照說明先定義類型,對兩個類進行改造。

internal class Book
{public BookId Id { get; set; }public string BookName { get; set; }public Author? Author { get; set; }public AuthorId AuthorId { get; set; }
}public readonly struct BookId
{public BookId(Guid value) => Value = value;public Guid Value { get; }
}
internal class Author
{public AuthorId Id { get; set; }public string Name { get; set; }public string Description { get; set; }}public readonly struct AuthorId
{public AuthorId(long value) => Value = value;public long Value { get; }
}

此時直接遷移肯定是會報錯的:

The property 'Author.Id' could not be mapped because it is of type 'AuthorId', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
67303010c5e650766233507a2dab7beb.png
遷移報錯

強類型 ID 在數據庫里面的表示還是原始的類型,我們還需要在?DbContext?中通過為類型定義值轉換器來實現轉換:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{configurationBuilder.Properties<AuthorId>().HaveConversion<AuthorIdConverter>();configurationBuilder.Properties<BookId>().HaveConversion<BookIdConverter>();
}private class AuthorIdConverter : ValueConverter<AuthorId, long>
{public AuthorIdConverter(): base(v => v.Value, v => new(v)){}
}private class BookIdConverter : ValueConverter<BookId, Guid>
{public BookIdConverter(): base(v => v.Value, v => new(v)){}
}

接著還沒結束,我們還需要?DbContext.OnModelCreating?中配置值轉換的,否則遷移后你會發現 Author 的主鍵自增沒有了,運行后的數據庫 Guid 還全變成 0 了。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.Entity<Author>().Property(author => author.Id).ValueGeneratedOnAdd();modelBuilder.Entity<Book>().Property(book => book.Id).ValueGeneratedOnAdd();
}

3. 使用 LessCode.EFCore.StronglyTypedId 簡化

通過上一小節我們看到,雖然支持了強類型 ID ,但是要實現起來需要自行配置的東西還是非常多得,用的越多,額外代碼的工作量也隨之增長。雖然是在自己代碼里 Ctrl CV 但是多執行幾次也說不定會一個疏忽而出錯。

因為在 GitHub Follow 了楊中科老師,所以在幾天前發現了我們這位寶藏大男孩提供的新工具?LessCode.EFCore.StronglyTypedId,開源地址:https://github.com/yangzhongke/LessCode.EFCore.StronglyTypedId,這個項目基于 source generator 技術,可以幫你生成額外的代碼,四舍五入約等于楊老師幫你把多余的代碼寫了。

根據說明文檔開始新的改造,首先安裝說需要的 Nuget 包,因為演示的 Demo 沒有分層,是一把梭哈的,直接安裝全部的包就可以了。分層的項目可以前往倉庫查看分層的使用文檔即可。

Install-Package LessCode.EFCore
Install-Package LessCode.EFCore.StronglyTypedIdGenerator

在改造上,只需要通過標識聲明這個類存在一個強類型 ID 即可,默認標識類型是 long ,對于?Author?類,只需要直接添加?[HasStronglyTypedId]?即可:

[HasStronglyTypedId]
internal class Author
{public AuthorId Id { get; set; }public string Name { get; set; }public string Description { get; set; }
}

對?Book?類使用的 Guid 類型 ID,可以使用 HasStronglyTypedId 的構造函數來制定標識類型:

[HasStronglyTypedId(typeof(Guid))]
internal class Book
{public BookId Id { get; set; }public string BookName { get; set; }public Author? Author { get; set; }public AuthorId AuthorId { get; set; }
}

對于 DbContext 的修改,只需要做簡單的配置即可,無需根據強類型 ID 的使用情況自行進行繁雜的轉換和配置,這些將由?LessCode.EFCore?根據?[HasStronglyTypedId]?的標識進行處理。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{base.OnModelCreating(modelBuilder);modelBuilder.ConfigureStronglyTypedId();
}protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{base.ConfigureConventions(configurationBuilder);configurationBuilder.ConfigureStronglyTypedIdConventions(this);
}

如此這般,可謂簡便了不少。俗話說的好(我說的):輪子用的好,程序下班早。趕快去試起來吧!

最后

更多 LessCode.EFCore.StronglyTypedId 的介紹可前往: https://github.com/yangzhongke/LessCode.EFCore.StronglyTypedId。

文章相關 Demo 地址:https://github.com/sangyuxiaowu/StronglyTypedId

References

[1]?.Net Core 視頻教程:?https://www.bilibili.com/video/BV1pK41137He/
[2]?改進的值生成:?https://learn.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-7.0/whatsnew#improved-value-generation

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280961.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280961.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280961.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何快速打造一款高清又極速的短視頻APP?

2019獨角獸企業重金招聘Python工程師標準>>> 整個短視頻的市場規模一直在增長&#xff0c;網絡數據顯示2018年已經突破100億大關&#xff0c;在2019年預測將超過200億。縱觀行業&#xff0c;在生活資訊、美食、搞笑、游戲、美妝等領域&#xff0c;短視頻流量巨大但競…

Django03: django加入APP

使用命令在已有project創建 1.創建 在manage.py同級運行命令 python manage.py startapp app01 2.django中加入app 在settings.py里的INSTALLED_APPS加入app01.apps.App01Config, INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttype…

[面經]春季跳槽面筋總結 [2018年3月17]

春季跳槽面筋總結 人人都說金三銀四&#xff0c;由于一些個人的原因&#xff0c;博主也在今年的三月份抽空面了幾家公司&#xff0c;這里來總結下學習到的東西。 先簡單的說下博主的情況&#xff1a; 2015年7月份畢業&#xff0c;到現在加上實習可以算三年工作經驗base武漢&…

如何將Windows 10帳戶還原為本地帳戶(在Windows Store劫持它之后)

If your Windows 10 user account is currently a Microsoft account (by your choice or because you got, one way or another, roped into it) it’s easy to revert it back to a local account if you know where to look. Read on as we show you how. 如果您的Windows 1…

【譯】Dapr 是一個“10倍好”平臺 !?

譯者注在正式閱讀本文之前&#xff0c;我們有必要先了解下什么是“10 倍好”。10 倍好理論最早出自彼得蒂爾的《從 0 到 1》&#xff0c;他說一個新創企業&#xff0c;要想獲得快速成長&#xff0c;其提供的解決方案要比現有方案好 10 倍以上&#xff0c;這個好 10 倍&#xff…

04.jQuery 基本語法筆記

jQuery是什么 jQuery是一個輕量級的、兼容多瀏覽器的JavaScript庫。jQuery使用戶能夠更方便地處理HTML Document、Events、實現動畫效果、方便地進行Ajax交互&#xff0c;能夠極大地簡化JavaScript編程。它的宗旨就是&#xff1a;“Write less, do more.“ jQuery引入到HTML …

1. ReactJS基礎(開發環境搭建)

本文主要介紹通過React官方提供的create-react-app腳手架進行開發環境的搭建。 1.安裝node環境(安裝過程這里不做介紹&#xff0c;可參考其他博文) 在cmd中輸入node -v 如果可以看到相應版本號&#xff0c;說明node環境安裝成功 2.npm全局安裝create-react-app腳手架 3.cmd命令…

軟件工程(2018)第一次作業

(1) 回顧你過去將近3年的學習經歷 當初你報考的時候&#xff0c;是真正喜歡計算機這個專業嗎&#xff1f; 在高中的時候&#xff0c;我們就開設了微機課&#xff0c;當時上課的內容不僅有Microsoft word,excel,powerpoint的使用&#xff0c;還有編程的基本入門&#xff0c;當時…

“云計算+DevOps”的正確打開方式

以我們的經驗看&#xff0c;技術和工具是很重要&#xff0c;但是技術和工具本身卻不能產生價值&#xff0c;而將DevOps和云計算結合卻可以。事實上&#xff0c;云計算的特性決定了&#xff0c;云計算和DevOps勢必如影隨形&#xff0c;而云計算與DevOps的結合也正在為企業用戶提…

微服務和分布式系統中的授權解決方案

本文是 《精讀 Mastering ABP Framework》 2.3 探索橫切關注點 - 使用授權和權限系統 一節的擴充內容&#xff0c;重點探討了授權在分布式和微服務系統中遇到的挑戰&#xff0c;以及 ABP Framework 中采用的解決方案。認證 & 授權? 認證&#xff08;Authentication&#x…

pat 團體天梯賽 L2-012. 關于堆的判斷

L2-012. 關于堆的判斷 時間限制400 ms內存限制65536 kB代碼長度限制8000 B判題程序Standard作者陳越將一系列給定數字順序插入一個初始為空的小頂堆H[]。隨后判斷一系列相關命題是否為真。命題分下列幾種&#xff1a; “x is the root”&#xff1a;x是根結點&#xff1b;“x a…

04-1.jQuery事件與補充each/data

目錄 事件 事件綁定 常用事件 阻止后續事件執行 補充 each .data() 事件 事件綁定 .on( events [, selector ],function(){}) events&#xff1a; 事件selector: 選擇器&#xff08;可選的&#xff09;function: 事件處理函數 普通綁定&#xff0c;沒有選擇器&#x…

【刷出存在感】鋒會圓桌現場

【編者按】本文為鋒會|路由器專場的圓桌全文&#xff08;有刪減&#xff09;。 圓桌嘉賓&#xff1a;&#xff08;自左向右依次&#xff09; 極路由聯合創始人 丁衣 知道創宇研究部總監&#xff08;安全專家&#xff09; 余弦 WRTnode創始人&#xff08;開源硬件領域&#xff0…

如何從命令行瀏覽和連接到無線網絡

() We are always on the lookout for geeky ways to impress our friends, and recently we came across a way to connect to our wireless network from the command prompt, so today we’ll show you how to do it as well. 我們一直在尋找令人印象深刻的方式來打動我們的…

html 基礎之canvas 和 localStorage

1&#xff0c;建立一個canvas 畫布&#xff1a; 1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <meta name"viewport" content"widthdevice-width, initial-scale1.0">…

國產數據助力金融行業維護信息安全

金融信息系統作為國家關鍵信息基礎設施&#xff0c;直接關系到國家經濟、社會的正常運行。長期以來&#xff0c;我國金融信息化依賴進口設備和系統&#xff0c;金融行業尤其是銀行業被IBM、HP、甲骨文等外商捆綁較深&#xff0c;金融行業信息化設備的軟硬件系統被外商壟斷。這等…

mysql查詢優化以及面試小結

mysql面試小結&#xff1a; 1.mysql的基本架構 2.mysql的索引 btree的原理 3.mysql的索引優化 4.mysql的sql查詢優化 慢查詢日志 Show prodile 全局查詢日志 5.mysql的主從復制 6.mysql的鎖機制 表鎖 行鎖轉載于:https://www.cnblogs.com/daiwei1981/p/10224934.html

05.Bootstrap導入基礎筆記

Bootstrap介紹 Bootstrap是Twitter開源的基于HTML、CSS、JavaScript的前端框架。 它是為實現快速開發Web應用程序而設計的一套前端工具包。 它支持響應式布局&#xff0c;并且在V3版本之后堅持移動設備優先。 為什么要使用Bootstrap&#xff1f; 在Bootstrap出現之前&…

etcd v3 集群——簡單配置

2019獨角獸企業重金招聘Python工程師標準>>> 一、etcd v3安裝&#xff1a; tar -axf etcd-v3.2.0-linux-amd64.tar.gz -C /usr/local/src/chmod ax /usr/local/src/etcd-v3.2.0-linux-amd64/etcd*cp -a /usr/local/src/etcd-v3.2.0-linux-amd64/etcd* /usr/local/bi…

windows變量延遲_Windows 10的2018年10月更新可能推遲到11月(這就是原因)

windows變量延遲Microsoft stopped offering Windows 10’s October 2018 Update on October 6, as it was deleting some people’s files. Now, another ugly data loss bug has reared its head, and it won’t be fixed until November. 微軟于10月6日停止提供Windows 10的…