电子商务网站建设案例教程,义乌网站建设与维护,长春高铁建站,网站结构设计的内容本文主要介绍 DDD 中的强类型 ID 的概念#xff0c;及其在 EF 7 中的实现#xff0c;以及使用 LessCode.EFCore.StronglyTypedId 这种更简易的上手方式。背景在杨中科老师 B 站的.Net Core 视频教程[1]其中 DDD 部分讲到了强类型 ID#xff08;Strongly-typed-id#xff09…本文主要介绍 DDD 中的强类型 ID 的概念及其在 EF 7 中的实现以及使用 LessCode.EFCore.StronglyTypedId 这种更简易的上手方式。背景在杨中科老师 B 站的.Net Core 视频教程[1]其中 DDD 部分讲到了强类型 IDStrongly-typed-id的概念也叫受保护的密钥guarded keys当时在 .NET 中的 DDD 实现是个悬而未决的问题之后我也一直在寻找相关的实现方案。非常高兴 .NET 7 的更新带来的 EF Core 7.0 的新增功能中就包含了改进的值生成[2]这一部分在自动生成关键属性的值方面进行了两项重大改进。下面我们通过几个例子来了解这部分的内容以及如何更简便的实现强类型。强类型 ID强类型 IDStrongly-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; }
}然后创建对应的 DbContextinternal class TestDbContext : DbContext
{public DbSetBook Books { get; set; }public DbSetAuthor Authors { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr Serverlocalhost;databasetest;uidroot;pwdroot;;var serverVersion new MySqlServerVersion(new Version(8, 0, 27));optionsBuilder.UseMySql(connStr, serverVersion);optionsBuilder.LogTo(Console.WriteLine);}}进行数据库迁移我们可以发现其创建的数据库表情况如下数据库然后在 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));其执行结果如下执行结果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.迁移报错强类型 ID 在数据库里面的表示还是原始的类型我们还需要在 DbContext 中通过为类型定义值转换器来实现转换protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{configurationBuilder.PropertiesAuthorId().HaveConversionAuthorIdConverter();configurationBuilder.PropertiesBookId().HaveConversionBookIdConverter();
}private class AuthorIdConverter : ValueConverterAuthorId, long
{public AuthorIdConverter(): base(v v.Value, v new(v)){}
}private class BookIdConverter : ValueConverterBookId, Guid
{public BookIdConverter(): base(v v.Value, v new(v)){}
}接着还没结束我们还需要 DbContext.OnModelCreating 中配置值转换的否则迁移后你会发现 Author 的主键自增没有了运行后的数据库 Guid 还全变成 0 了。protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.EntityAuthor().Property(author author.Id).ValueGeneratedOnAdd();modelBuilder.EntityBook().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/StronglyTypedIdReferences[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