You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

EF Core中如何创建无筛选条件的多列唯一索引(SQL Server)

解决EF Core自动给唯一索引加过滤条件的问题

嘿,我来帮你搞定这个问题!首先得搞清楚为什么EF Core会自动生成那个筛选条件——这是它的默认行为:当你创建包含可空列的唯一索引时,EF Core默认认为你想允许该列值为NULL的行存在重复的索引组合,所以自动加上了[DeletionMarker] IS NOT NULL的过滤。但你要的是取消这个筛选,让所有行都遵守唯一约束,下面是规范的实现方式:

方法1:显式移除默认过滤条件

在定义索引的时候,添加.HasFilter(null)就能覆盖EF Core的默认逻辑,这样生成的迁移代码就不会带那个筛选条件了:

// 你的实体配置代码
modelBuilder.Entity<YourEntity>()
    .HasIndex(r => new { r.Name, r.DeletionMarker })
    .IsUnique()
    .HasFilter(null); // 这行是关键,告诉EF Core不要加默认过滤

生成的迁移代码会变成这样,完全符合你的要求:

migrationBuilder.CreateIndex(
    name: "whatever",
    table: "whatever",
    columns: new[] { "Name", "DeletionMarker" },
    unique: true);

重要提醒:SQL Server中NULL的特殊行为

这里得给你提个醒:SQL Server的唯一索引里,NULL值被视为不相等。也就是说,哪怕你去掉了筛选条件,还是可以有多行Name相同且DeletionMarker为NULL的记录——因为SQL里NULL != NULL是成立的。

如果你的真实需求是不允许两行DeletionMarker均为NULL时Name重复(也就是未删除的记录Name必须唯一),那上面的方法还不够,得调整索引逻辑:

方案A:用计算列统一NULL值

创建一个计算列,把DeletionMarker的NULL映射成一个固定字符串,然后基于这个计算列和Name创建唯一索引:

// 先定义一个计算列
modelBuilder.Entity<YourEntity>()
    .Property<string>("NormalizedDeletionMarker")
    .HasComputedColumnSql("ISNULL(CAST(DeletionMarker AS NVARCHAR(MAX)), 'DELETED_NULL')")
    .IsStored();

// 基于计算列创建唯一索引
modelBuilder.Entity<YourEntity>()
    .HasIndex(r => new { r.Name, "NormalizedDeletionMarker" })
    .IsUnique();

方案B:创建两个针对性的唯一索引

分别给未删除和已删除的记录设置约束:

// 强制未删除行(DeletionMarker为NULL)的Name唯一
modelBuilder.Entity<YourEntity>()
    .HasIndex(r => r.Name)
    .IsUnique()
    .HasFilter("[DeletionMarker] IS NULL");

// 强制已删除行的(Name, DeletionMarker)组合唯一(因为DeletionMarker是Guid,本身唯一)
modelBuilder.Entity<YourEntity>()
    .HasIndex(r => new { r.Name, r.DeletionMarker })
    .IsUnique()
    .HasFilter("[DeletionMarker] IS NOT NULL");

这样就能精准实现你要的“未删除记录Name不重复,已删除记录允许Name重复但组合唯一”的需求啦。

内容的提问来源于stack exchange,提问作者Andrea

火山引擎 最新活动