我有以下实体模型:
public class RoutePermission
{
public Guid? UserId { get; set; }
public Guid? DistrictId { get; set; }
public Guid RouteId { get; set; }
public bool Admin { get; set; }
}
我想添加一个唯一的约束来实现以下行为:
{ routeId: 1, userId: 1, districtId: null, admin: true } // Allowed
{ routeId: 1, userId: null, districtId: 1, admin: true } // Allowed
{ routeId: 2, userId: 1, districtId: null, admin: true } // Allowed
{ routeId: 2, userId: 1, districtId: null, admin: true } // Not Allowed
问题是插入第四条记录时没有抛出错误。
这是我开始的模型验证:
modelBuilder.Entity<RoutePermission>()
.HasIndex(rp =>
new { rp.RouteId, rp.DistrictId, rp.UserId }
)
.IsUnique();
没有骰子。我做了很多研究,并且发现了
.HasFilter(null)
,据说它会覆盖在任何已验证字段为空时跳过验证的默认行为。我明白了!不,还是什么都没有。
我终于发现真正的问题是什么了。 Postgres 认为
null
值与另一个值indistinct,因此 null != null (就像 JavaScript 中的对象一样)。现在我明白了为什么 Postgres 没有为上面示例中的第四条记录抛出任何错误。但如何解决呢?
ChatGPT 建议使用
COALESCE
将空值替换为等于另一个类似值的值。虽然 null != null
,但如果我们用 0 替换空值,0 = 0
并且验证应该按预期进行。唉,还是没有骰子。
像往常一样,这个解决方案让我感觉很愚蠢。
最终的、复杂的 SQl-custom-filter-bypassing-ef-core-limitations?
.AreNullsDistinct(false);
😒
最终解决方案/#TDLR
modelBuilder
.Entity<RoutePermission>()
.HasIndex(rp =>
new { rp.RouteId, rp.DistrictId, rp.UserId }
)
.IsUnique()
.AreNullsDistinct(false);