Primitive Obsession - 具有自动增量的强类型 int ID

Posted

技术标签:

【中文标题】Primitive Obsession - 具有自动增量的强类型 int ID【英文标题】:Primitive Obsession - Strongly typed int ID with auto increment 【发布时间】:2020-02-25 23:43:51 【问题描述】:

如何获得强类型的Id...

public sealed class FileUploadId

    public int Value  get; 

    public FileUploadId(int value)
    
        Value = value;
    

...在我的FileUpload 类中使用...

public class FileUpload : EntityBase, IAggregateRoot

    private FileUpload()  /* Required by EF */ 

    public FileUpload(string name, string relativePath, FileUploadType type, string contentType, long? size = null)
    
        /* Guard clauses... */

        Id = new FileUploadId(0);

        /* Other properties... */
    

    public FileUploadId Id  get; 

    /* Other properties... */

...使用身份(int 自动递增)?

我在TypeConifiguration 中尝试了ValueGeneratedOnAdd(),但没有成功...

public class FileUploadTypeConfig : IEntityTypeConfiguration<FileUpload>

    public void Configure(EntityTypeBuilder<FileUpload> builder)
    
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id).HasConversion(x => x.Value, x => new FileUploadId(x)).ValueGeneratedOnAdd();

        /* Other configurations... */
    

我知道 HiLo 算法还有另一种选择。但我想让它使用默认的int id 增量。这有可能吗?

【问题讨论】:

为什么你认为你仍然需要FileUploadId 类?如果它所做的只是公开一个只读 int 属性,为什么不在 FileUpload 类上使用一个只读 int 属性呢? FileUploadId 有什么好处? 这是关于“原始痴迷”的。你可以在这里阅读:andrewlock.net/… 谢谢,因为你,我今天学到了一些新东西!然而,我确实认为,虽然博客文章中提出的观点是有效的,并且提出的解决方案也是有效的,但这种方法在许多情况下可能是一种巨大的矫枉过正。 是的,我完全同意。在大多数情况下,这种方法是矫枉过正的。但是,当您从头开始一个包含大型业务领域的新项目时,它可能对干净的代码库非常有帮助。 所以想法是在自定义类中包含一些原始值,例如主键和外键,因此类匹配可以询问您“为什么要尝试将 CustomerID 分配给 OrderID 字段/属性?”有趣的想法。可以适当地为它制作一个通用的。 【参考方案1】:

我设法通过以下方式获得具有自动递增整数的强类型 ID,可用于 .net6 和 EFCore6:

配置.HasConversion() 添加.ValueGeneratedOnAdd() 添加.Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore) 确保强类型 id 永远不会为空

编辑: 这种方法有一个陷阱。

这与更改跟踪有关。从 ef core 3.0 和 this GitHub issue 开始,当您对键使用自动递增且键值不是默认值时,实体将以“已修改”状态而不是“已添加”状态添加。这是当前解决方案的一个问题,因为我们从未为强类型 ID 设置 null。我们必须手动开始跟踪处于“已添加”状态的实体(使用 DbContext Add 方法),其他类型的自动跟踪将不起作用(例如,将实体添加到导航属性集合中以实现一对多关系)。正在通过this GitHub issue 跟踪对此的官方支持。

【讨论】:

【参考方案2】:

老实说,在 EF 中使用带有 SQL 的强类型变量是一种真正的痛苦。我看到的最好的解释可以在this blog post by Andrew Lock找到。

问题的核心?使用强类型值会导致在客户端执行查询过滤(WHERE ID = value 子句),要求进程从数据库中检索所有记录以在本地执行选择。

解决方案的核心是什么?使用 ValueConverter 将强类型值转换为正确的 SQL Server 值。

细节很丰富。有关如何执行此操作的详细信息,请参阅参考文章。

【讨论】:

这实际上并没有解决我的问题。我的问题不是将强类型值转换为本地人。我已经知道 Andrews 博客系列。但他总是使用手动生成的Guid。我只想使用一个int,它是自动递增(插入之前不是手动创建的)。【参考方案3】:

我建议不要让 EF 处理强类型 ID。它不能很好地处理它,并且在某些情况下它会尝试过滤内存中的内容。

但是您可以同时拥有这两个属性,一个用于 EF 查询,一个用于其他所有属性。

class FileUpload

    public int InternalId  get; private set; 
    public FileUploadId Id 
    
        get  return new FileUploadId(InternalId); 
        set  InternalId = value.Value; 
    

它是泄漏的,但它有效。

【讨论】:

以上是关于Primitive Obsession - 具有自动增量的强类型 int ID的主要内容,如果未能解决你的问题,请参考以下文章

为应用程序命令公开强类型 ID?

Primitive篇(贴图)

Codeforces 869 C The Intriguing Obsession

UVA 12950 : Even Obsession(最短路Dijkstra)

hdu 5943 Kingdom of Obsession 二分图匹配+素数定理

CF869C The Intriguing Obsession