首页 > 其他分享 >我开发的开源项目,让.NET7中的EFCore更轻松地使用强类型Id

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

时间:2022-11-29 16:23:41浏览次数:64  
标签:Core PersonId Value public 类型 EFCore NET7 Id

在领域驱动设计(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]即可。

项目地址:https://github.com/yangzhongke/LessCode.EFCore.StronglyTypedId

 

下面我用一个把所有代码都写到一个控制台项目中的例子来演示它的用法,多项目分层等更复杂的用法请见项目文档以及项目中的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);

}
复制代码

 强类型Id让我们能够更好的在EFCore中实现DDD,我开源的这个项目能够让开发者只要在实体类上标注一行[HasStronglyTypedId]就可以完成强类型Id的使用。希望它能够帮到你,欢迎把它分享到你所在的技术社区。

 

转 https://www.cnblogs.com/rupeng/p/16934316.html

标签:Core,PersonId,Value,public,类型,EFCore,NET7,Id
From: https://www.cnblogs.com/wl-blog/p/16935691.html

相关文章

  • 工具---idea控制台乱码永久解决
    我们打开IntelliJIDEA在本地的安装目录,找到下面两个文件,使用记事本打开,在最后一行添加-Dfile.encoding=UTF-8 即可。在重启之后运行程序检查控制台信息,问题解决! ......
  • flutter使用getwidget的ui组件库的底部导航用法
    参考github上getwidget-app-kit import'package:flutter/material.dart';import'package:getwidget/getwidget.dart';import'package:flutter/cupertino.dart';......
  • 使用LRU算法缓存图片,android 3.0
    在您的UI中显示单个图片是非常简单的,如果您需要一次显示很多图片就有点复杂了。在很多情况下(例如使用ListView,GridView或者 ​​​ViewPager​​​控件),显示在屏幕......
  • android viewgroup事件分发机制
    1、案例首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout:[java]​​viewplain​​​​copy​​package  importimportimportimportimpor......
  • android混淆
    为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤:1.大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt......
  • android自定义view实现progressbar的效果
    一键清理是很多Launcher都会带有的功能,其效果也比较美观。实现方式也许有很多中,其中常见的是使用图片drawable来完成的,具体可以参考这篇文章:​​模仿......
  • android studio添加project libs库步骤
    在Eclipse中选择要导出的项目,然后依次选择菜单file->export->Android->GenerateGradlebuild files.之后依次点击next到finish即可回到Androidstudio打开settin......
  • 不好意思,UUID 该换了!
    ULID:UniversallyUniqueLexicographicallySortableIdentifier(通用唯一词典分类标识符)UUID:UniversallyUniqueIdentifier(通用唯一标识符)为什么不选择UUIDUUID目前有......
  • 拓端tecdat|【视频】Rapidminer辅导关联规则和文本挖掘模型对评论数据进行分析
    关联规则是if/then语句,可帮助发现看似无关的数据之间的关系。关联规则的一个示例是“如果客户购买鸡蛋,那么他有80%的可能性也购买牛奶”。关联规则包含......
  • android守护进程
    Service组件在android开发中经常遇到,其经常作为后台服务,需要始终保持运行,负责处理一些必要(见不得人)的任务。而一些安全软件,如360等,会有结束进程的功能,如果不做Service的保持......