我开发的开源项目,让.NET7中的EFCore更轻松地使用强类型Id

news2025/4/9 9:33:42

在领域驱动设计(DDD)中,有一个非常重要的概念:“强类型Id”。使用强类型Id来做标识属性的类型会比用int、Guid等通用类型能带来更多的好处。比如有一个根据根据Id删除用户的方法的签名如下:

void RemoveById(long id);

我们从方法的参数看不出来id代表什么含义,因此如果我们错误地把货物的id传递给这个方法,那么也是可以的。这样用long等通用类型来表示标识属性会让参数等的业务属性弱化。

而如果我们自定义一个UserId类型,如下:

class UserId

{

public long Value{get;init;}

public UserId(long value)

{

    this.Value=value;

}

}

这样User类的定义中Id属性的类型就从long变成了UserId类型,如下:

class User

{

   public UserId Id{get;}

   public string Name{get;set;}

}

对应的RemoveById方法的签名也变成了:

void RemoveById(UserId id);

这样不仅能一看就看出来id参数代表的业务含义,也能避免“把货物Id的值传递给用户Id参数”这样的问题。

在.NET 6及之前,Entity Framework Core(简称EF Core)中很难优美地实现强类型Id。在.NET7中,EF Core中提供了对强类型Id的支持,具体用法请参考EF Core官方文档中“Value generation for DDD guarded types”这部分内容。

尽管EF Core已经内置了对强类型Id的支持,但是它需要程序员编写非常多的代码。比如一个比较完善的强类型Id类的代码就要编写如下30多行代码:

public readonly struct PersonId

{

            public Guid Value { get; }

            public PersonId(Guid value)

            {

                        Value = value;

            }

 

            public override string ToString()

            {

                        return Convert.ToString(Value);

            }

 

            public override int GetHashCode()

            {

                        return Value.GetHashCode();

            }

 

            public override bool Equals(object obj)

            {

                        if (obj is PersonId)

                        {

                                    PersonId objId = (PersonId)obj;

                                    return Value == objId.Value;

                        }

                        return base.Equals(obj);

            }

 

            public static bool operator ==(PersonId c1, PersonId c2)

            {

                        return c1.Equals(c2);

            }

 

            public static bool operator !=(PersonId c1, PersonId c2)

            {

                        return !c1.Equals(c2);

            }

}

还要编写一个ValueConverter类以及配置自定义的ValueGenerator……需要编写的代码的复杂程度让想使用强类型Id的开发者望而却步。

正因为这一点,所以连微软的文档中都​警告到"强类型Id会增加代码的复杂性,请谨慎使用"。幸好,这个世界有我!

为了解决这个问题,我基于.NET的SourceGenerator技术编写了一个开源项目,这个开源项目会在编译时自动生成相关的代码,开发人员只要在实体类上标注一个[HasStronglyTypedId]即可。

项目地址:GitHub - yangzhongke/LessCode.EFCore.StronglyTypedId: Automatically generate Types for Strongly Typed Id in Entity Framework Core

下面我用一个把所有代码都写到一个控制台项目中的例子来演示它的用法,多项目分层等更复杂的用法请见项目文档以及项目中的Examples文件夹中的内容。

注意:这个项目可能会随着升级而用法有所变化,具体用法请以最新官方文档为准。

用法:

1、 新建一个.NET7控制台项目,然后依次安装如下这些Nuget包:LessCode.EFCore、LessCode.EFCore.StronglyTypedIdCommons、LessCode.EFCore.StronglyTypedIdGenerator。当然我们的项目要使用SQLServer以及使用EF core的migration,所以还要安装如下的Nuget包:Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools。

2、 项目中新建一个实体类型Person

[HasStronglyTypedId]

class Person

{

            public PersonId Id { get; set; }

            public string Name { get; set; }

}

我们注意到Person上标注的[HasStronglyTypedId(typeof(Guid))],它代表这个类启用强类型Id,编译器在编译的时候自动生成一个名字叫PersonId的类,所以我们就声明了一个名字叫Id、类型为PersonId的属性来表示实体的标识。

PersonId在数据库中保存的默认是long类型,如果想保存为Guid类型,就可以写成[HasStronglyTypedId(typeof(Guid))]。

编译一下项目,如果能够编译成功,我们反编译生成的dll,就能看到dll中自动生成了PersonId、PersonIdValueConverter两个类。

 

3、 编写DbContext,代码如下:

using LessCode.EFCore;

class TestDbContext:DbContext

{

            public DbSet<Person> Persons { get; set; }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

            {

                        optionsBuilder.UseSqlServer(自己的连接字符串);

            }

 

            protected override void OnModelCreating(ModelBuilder modelBuilder)

            {

                        base.OnModelCreating(modelBuilder);

                        modelBuilder.ConfigureStronglyTypedId();

            }

 

            protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)

            {

                        base.ConfigureConventions(configurationBuilder);

                        configurationBuilder.ConfigureStronglyTypedIdConventions(this);

            }

}

4、 进行数据库的迁移等操作,这部分属于EF Core的标准操作,我不再介绍。对EF Core的用法不熟悉的朋友,请到哔哩哔哩、youtube等平台搜索“杨中科 .NET Core教程”。

5、 编写代码进行测试

using TestDbContext ctx = new TestDbContext();

Person p1 = new Person();

p1.Name = "yzk";

ctx.Persons.Add(p1);

ctx.SaveChanges();

PersonId pId1 = p1.Id;

Console.WriteLine(pId1);

Person? p2 = FindById(new PersonId(1));

Console.WriteLine(p2.Name); 

Person? FindById(PersonId pid)

{

    using TestDbContext ctx = new TestDbContext();

    return ctx.Persons.SingleOrDefault(p => p.Id == pid);

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/52925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【毕业设计】9-基于STM32无刷直流电机控制器的设计仿真与实现(原理图+源码+仿真工程+论文+PPT+参考英文文献)

毕业设计】基于STM32无刷直流电机控制器的设计仿真与实现&#xff08;原理图源码仿真工程论文PPT参考英文文献&#xff09; 文章目录毕业设计】基于STM32无刷直流电机控制器的设计仿真与实现&#xff08;原理图源码仿真工程论文PPT参考英文文献&#xff09;任务书设计说明书摘要…

能迪科技智能控制系统对中央空调进行精准、单独调控医院案例

案例背景​ 梅州市妇女儿童医院新院区&#xff08;以下简称“新院区”&#xff09;是省、市重点项目工程&#xff0c;建设地点位于江南新城客都大道北侧&#xff0c;一期项目总投资4.8亿元&#xff0c;占地面积50亩&#xff0c;总建筑面积87000平方米&#xff0c;按照三级妇幼保…

blender cycles引擎

文章目录简介属性一 Scene采样二 光程最多反弹次数钳制焦散快速GI近似三 体积步进速率四 曲线简介 1 cycles与EV的区别在于cy是传统渲染引擎&#xff0c;效果好&#xff0c;速度慢&#xff0c;ev是实时引擎&#xff0c;速度快&#xff0c;效果差 2 切换渲染引擎&#xff0c;属…

基于51单片机智能IC卡水表控制系统(仿真+源程序+全套资料)

资料编号&#xff1a;200 功能介绍&#xff1a; 采用51单片机作为主控CPU&#xff0c;使用按键进行模拟冲卡&#xff08;模拟缴费冲卡&#xff09;&#xff0c;通过按键来控制当前是否使用自来水&#xff0c;并且LCD1602实时显示当前自来水可用量剩余多少&#xff0c;当自来水…

GhMYB7促进棉纤维中次生壁纤维素的积累

文章信息 题目&#xff1a;GhMYB7 promotes secondary wall cellulose deposition in cotton fibres by regulating GhCesA gene expression through three distinct cis-elements 刊名&#xff1a;New Phytologist 作者&#xff1a;Junfeng Huang&#xff0c;Wenliang Xu e…

图文详解Linux基础经典教程(10)——阿里云安装开发工具

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 概述 之前&#xff0c;我们已经介绍在本地CentOS上安装JDK、Tomcat、MySQL等开发工具。接下来&#xff0c;我们介绍在阿里云安装这些开发工具。 购买阿里云 请在阿里云 h…

自然语言处理NLP——ERNIE-M:基于回译机制的“预训练-微调”多语言模型

目录 系列文章目录 一、背景介绍 1.多语言任务 1.1 多语言任务定义 1.2 多语言任务难题 2.多语言模型 2.1 多语言模型定义与原理 2.2 多语言模型困难 3.论文简介 3.1 背景与开发动机 3.2 论文梗概 3.3 论文贡献与成就 二、相关工作 1.预训练方法 1.1 预训练方法…

Postman之Newman命令行运行脚本生成HTML报告

目录 一、Newman的下载安装 二、Newman生成Html报告 三、执行脚本准备 3.1.导出项目集脚本 3.2.导出环境变量 3.3.导出全局变量 3.4.data数据驱动文件 3.5.文件存储 四、Newman运行命令简介 4.1.运行命令&#xff1a;newman run 4.2.常用参数&#xff1a; 4.3.执行…

【SSM框架】依赖注入

&#x1f353;个人主页&#xff1a;个人主页 &#x1f352;系列专栏&#xff1a;SSM框架 目录 1.依赖注入之setter注入 2.依赖注入之构造器注入 3.特殊值处理 4.为类类型属性赋值 5.为数组类型属性赋值 1.依赖注入之setter注入 ①创建学生类Student package com.atguigu.s…

09【SpringMVC的Json支持】

文章目录三、Json的支持3.1 响应json3.1.1 ResponseBody3.1.2 JsonIgnore3.1.3 JsonFormat3.1.4 ResponseEntity3.1.5 作用在类上3.1.6 RestController3.2 请求Json3.2.1 RequestBody3.2.2 HttpEntity3.2.3 封装Json数据三、Json的支持 SpringMVC支持自动将JSON转换成Java对象…

ubuntu更换清华源

进入&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 选择你的ubuntu版本 vi /etc/apt/sources.list 如果执行apt update 报镜像源证书错误 &#xff08;1&#xff09;将/etc/apt/sources.list中的https改为http, 然后执行apt update&#xff1b; &#…

Linux进程状态

目录 一、普适操作系统的进程状态 1.什么是进程状态 2.三种重要的进程状态 &#xff08;1&#xff09;运行状态 &#xff08;2&#xff09;阻塞状态 &#xff08;3&#xff09;挂起状态 二、Linux源代码中的进程状态 三、Linux进程状态 1.运行状态 2.睡眠状态&#x…

AI加速(九): 深度理解吞吐量和延时

前文回顾&#xff1a; AI加速&#xff08;二&#xff09;| 计算机存储和计算的分离 AI加速&#xff08;三&#xff09;| 每条指令都是流水线的工人 AI加速&#xff08;四&#xff09;| 衣柜般的分层存储设计 AI加速&#xff08;五&#xff09;| 一个例子看懂流水——从指令到…

Java多线程之Thread和Runnable以及Callable接口多线程的简单实现(适合小白入门,十分简单)

Java多线程之Thread和Runnable一、介绍1、程序2、进程3、线程4、整体思路5、注意二、Thread1、思路流程2、样例代码3、多线程下载图片三、Runnable1、思路流程2、样例代码3、多线程下载图片四、Callable接口&#xff08;仅作了解&#xff09;1、具体流程2、优点3、样例五、两者…

视频编解码 — SVC可伸缩性

目录 SVC分层 时域SVC 空域SVC 在一对多的情况下&#xff0c;根据每个接收端的带宽不同&#xff0c;灵活调整发送码率 SVC分层 第0层&#xff0c;最底层&#xff0c;可以独立进行编解码&#xff0c;不依赖第1&#xff0c;第2层第1层&#xff0c;依赖于第0层第2层&#xff…

Linux中的/proc文件系统详解(C/C++代码实现)

Linux /proc这个特殊的目录包含有关Linux系统的所有详细信息&#xff0c;包括其内核、进程和配置参数。通过研究/proc目录&#xff0c;可以了解Linux命令的工作原理&#xff0c;甚至可以执行一些管理任务。 走进Linux的/proc目录 今天&#xff0c;我们将查看/proc目录并熟悉它…

最新版 苹果 IOS AppStore证书申请全流程 包括p12文件

第一步 登陆开发者网站并进入证书管理页。 辅助工具网站地址&#xff1a;http://www.applicationloader.net/ 第二步 添加【Certificates】证书。在这里插入图片描述 第三步 进入添加页后&#xff0c;根据需求选择。 第四步 选择好环境之后&#xff0c;这里需要选择一个…

GitHub上传本地程序操作的问题解决汇总

1、新建远程仓库 2、提交本地程序到GitHub 完整命令汇总&#xff0c;这些命令的顺序已解决目前Git提交所遇到的下面问题 存在的问题展示部分没贴出来&#xff0c;可以先按命令顺序走一遍流程。 git init //把这个目录变成Git可以管理的仓库git checkout -b main //切换至main默…

如何零基础自学 Python ?听我娓娓道来

如何零基础自学 Python &#xff1f; 前言 今天这篇文章是对初学者的一篇学习资料分享与总结&#xff01; 简单介绍下笔者的自学之路 起初学习 Python 是在 2020 年&#xff0c;那时临近毕业&#xff0c;由于面临着找工作的不确定性&#xff0c;便学习了相关的网络安全知识&…

计组作业笔记【 总线 】

系统总线按传输内容不同分为数据总线&#xff0c;地址总线&#xff0c;控制总线。 片内总线与系统总线是并列关系&#xff0c;与通信总线一同作为总线按功能的分类。 所以选B 总线复用是指一种信号线在不同时间传输不同的信息。 所以该题选A。 当某设备正获得了总线控制权时…