使用C#9中records作为强类型ID的实例教程

强类型ID

实体通常是整数,GUID或者string类型,因为数据库直接支持这些类型,但是,如果实体的ID的类型是一样的,比如都是整数的ID,这有可能会出现ID值传错的问题,看下边的示例。

public void AddProductToOrder(int orderId, int productId, int count)
{
 ...
}

...

// 这个地方,参数传错了
AddProductToOrder(productId, orderId, int count);

上面的代码可以很好地通过检查并编译,但是在运行的时候就出问题了,这是逻辑bug。

幸运的是,可以定义强类型id来解决这个问题,这个想法很简单,为每个实体的ID声明一个特定的类型,现在需要这样写:

// 使用强类型ID代替整数ID
public void AddProductToOrder(OrderId orderId, ProductId productId, int count)
{
 ...
}

...

// 这个地方,参数传错了
AddProductToOrder(productId, orderId, int count);

在上面的代码中,我们犯了与第一个示例相同的错误(交换productId和orderId),但是在这种情况下,类型不同,因此编译器会捕获该错误并报告错误,我们仍然需要对其进行修复,但是至少在生产中并没有爆炸。

编写一个强类型的id

public readonly struct ProductId : IEquatable<ProductId>
{
 public ProductId(int value)
 {
  Value = value;
 }

 public int Value { get; }

 public bool Equals(ProductId other) => other.Value == Value;
 public override bool Equals(object obj) => obj is ProductId other && Equals(other);
 public override int GetHashCode() => Value.GetHashCode();
 public override string ToString() => $"ProductId {Value}";
 public static bool operator ==(ProductId a, ProductId b) => a.Equals(b);
 public static bool operator !=(ProductId a, ProductId b) => !a.Equals(b);
}

上面的代码没什么难的,但是如果每个实体都需要的话,那确实有点麻烦,在C# 9 可以使用source generators来完成这些,但是C# 9还引入了另一个功能,使用起来更方便。

Record类型

Record 类型是具有内置不变性和值语义的引用类型,它和上面我们写的强类型是一样的(手动写的成员实现Equals,GetHashCode等等),在代码中使用也非常简洁, 如果我们ProductId使用record重写类型,就是下边这样:

public record ProductId(int Value);

是的,您没看错,这是一行,而上面的代码是一大段,它完成了我们手动执行的所有操作(实际上,还多了很多!)。

主要区别在于:我们的手动实现是struct,即值类型,但是记录是引用类型,这意味着它们可以为null,这可能不是主要问题,尤其是在使用可为空的引用类型的情况下,但是要知道这一点。

现在为模型中的每个实体编写一个强类型的id是不是很简单,使用Record 非常方便,当然,还有其他问题需要考虑,例如JSON序列化,与Entity Framework Core一起使用等,但这是另一篇文章的故事!

总结

到此这篇关于使用C#9中records作为强类型ID的文章就介绍到这了,更多相关C#9 records作强类型ID内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

原文作者: thomas levesque

原文链接:https://thomaslevesque.com/2020/10/30/using-csharp-9-records-as-strongly-typed-ids/

(0)

相关推荐

  • 使用C#9中records作为强类型ID的实例教程

    强类型ID 实体通常是整数,GUID或者string类型,因为数据库直接支持这些类型,但是,如果实体的ID的类型是一样的,比如都是整数的ID,这有可能会出现ID值传错的问题,看下边的示例. public void AddProductToOrder(int orderId, int productId, int count) { ... } ... // 这个地方,参数传错了 AddProductToOrder(productId, orderId, int count); 上面的代码可以很好地

  • SqlServer 在事务中获得自增ID的实例代码

      SqlServer 在事务中获得自增ID实例代码 在sqlserver 中插入数据时,如何返回自增的主键ID,方式有很多,这里提供一种. 代码如下: USE tempdb go CREATE TABLE table1 ( id INT, employee VARCHAR(32) ) go INSERT INTO table1 VALUES(1, 'one') INSERT INTO table1 VALUES(2, 'two') INSERT INTO table1 VALUES(3, 't

  • C++类中的static和const用法实例教程

    static和const是C++程序设计中非常重要的概念,本文实例列举了C++类中的static和const的规则和用法.供大家参考借鉴.具体说明如下: 首先以代码用来举例说明.示例代码如下: class A { public: A():m(10) //const成员必须在构造函数的初始化构造列表中初始化 { q = 40; } void fun1()const { m++; //错误.const成员是常量,不能改变其值. n++; //正确.static变量n属于类,但是每个对象的函数都可以访

  • Android系统中的蓝牙连接程序编写实例教程

    Bluetooth结构 1.JAVA层 frameworks/base/core/java/android/bluetooth/ 包含了bluetooth的JAVA类. 2.JNI层 frameworks/base/core/jni/android_bluetooth_开头的文件 定义了bluez通过JNI到上层的接口. frameworks/base/core/jni/android_server_bluetoothservice.cpp 调用硬件适配层的接口system/bluetooth/

  • Android App开发中自定义View和ViewGroup的实例教程

    View Android所有的控件都是View或者View的子类,它其实表示的就是屏幕上的一块矩形区域,用一个Rect来表示,left,top表示View相对于它的parent View的起点,width,height表示View自己的宽高,通过这4个字段就能确定View在屏幕上的位置,确定位置后就可以开始绘制View的内容了. View绘制过程 View的绘制可以分为下面三个过程: Measure View会先做一次测量,算出自己需要占用多大的面积.View的Measure过程给我们暴露了一个

  • Android中使用DialogFragment编写对话框的实例教程

    Android提供alert.prompt.pick-list,单选.多选,progress.time-picker和date-picker对话框,并提供自定义的dialog.在Android 3.0后,dialog基于fragment,并对之前版本提供兼容支持库,也就是说对于开发者而言,dialog是基于DialogFragment的,但此时需要在应用中加入相关的兼容库. 和Windows或者网页JS的Dialog不同,Android的dialog是异步的,而不是同步的.对于同步的dialog

  • 在JavaScript中重写jQuery对象的方法实例教程

    jQuery是一个款非常优秀的类库,它给我们解决了很多的客户端编程,但是任何东西都不是万能的,当它不能满足我们的需求时我们就需要对它进行重写,同时也不要影响其原有的功能或者修改其原有的功能:比如我现在的web应用程序大多数时候的数据交互都是通过Ajax来完成的,这样就可以将一些隐藏字段的数据保存在HTML标签的属性中,使HTML标签的代码量减少,如:ID,Timestamp等等,这些不需要用户输入但又不得不提交的字段,通过表单提交的做法是 <input name="ID" val

  • vue中如何创建多个ueditor实例教程

    前言 前一段时间公司Vue.js项目需要使用UEditor富文本编辑器,在百度上搜索一圈没有发现详细的说明,决定自己尝试,忙活了一天终于搞定了. 具体可以参考这篇文章:http://www.jb51.net/article/118413.htm ueditor是百度编辑器,官网地址:http://ueditor.baidu.com/website/ 完整的功能演示,可以参考:http://ueditor.baidu.com/website/onlinedemo.html 最近工作中要求升级,需要

  • Ruby和Ruby on Rails中解析JSON格式数据的实例教程

    Ruby解析JSON Ruby解析Json例子: json = '["a", "B", "C"]' puts "Unsafe #{unsafe_json (json).inspect}" #输出Unsafe ["a", "B", "C"] Ruby解析Json把上面的json字符串解析成Array.这样的方法并不安全,比如: json = 'puts "Da

  • Python中optionParser模块的使用方法实例教程

    本文以实例形式较为详尽的讲述了Python中optionParser模块的使用方法,对于深入学习Python有很好的借鉴价值.分享给大家供大家参考之用.具体分析如下: 一般来说,Python中有两个内建的模块用于处理命令行参数: 一个是 getopt,<Deep in python>一书中也有提到,只能简单处理 命令行参数: 另一个是 optparse,它功能强大,而且易于使用,可以方便地生成标准的.符合Unix/Posix 规范的命令行说明. 示例如下: from optparse impo

随机推荐