当前位置 博文首页 > 文章内容

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

    作者:shunshunshun18 栏目:未分类 时间:2021-01-17 14:43:43

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    强类型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一起使用等,但这是另一篇文章的故事!

    总结

    原文作者: thomas levesque

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