强类型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内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

原文作者: thomas levesque

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