我的实体拥有一个允许为空的属性。但是,如果它不为空,则必须是唯一的。换句话说,这个列是唯一的,但允许多个空值。
我已经尝试过:
config.Property(p => p.ProductId).IsRequired(false);
我记得在Core EF之前,我曾经苦苦挣扎地让它正常工作。
这个可能吗?我该如何配置实体?
我的实体拥有一个允许为空的属性。但是,如果它不为空,则必须是唯一的。换句话说,这个列是唯一的,但允许多个空值。
我已经尝试过:
config.Property(p => p.ProductId).IsRequired(false);
我记得在Core EF之前,我曾经苦苦挣扎地让它正常工作。
这个可能吗?我该如何配置实体?
是的,您可以使用EF Core来实现这一点,因为唯一索引默认会被创建为过滤索引(WHERE ... IS NOT NULL)
config.Entity<Product>()
.HasIndex(b => b.ProductId)
.IsUnique();
我知道这篇文章有点旧了,但是对于那些现在才发现它的人来说,我在EF Core 5.0中看到的默认行为并不是被接受答案所描述的那样。你可以明确地为一个索引指定过滤条件,下面是我刚刚创建的一个例子:
modelBuilder.Entity<Reef>(etb =>
{
// ...
etb.HasIndex(r => r.Label)
.HasFilter("Label IS NOT NULL")
.IsUnique();
// ...
});
编辑:
我认为我已经找到了完整的答案。如果您有一个可空值类型的属性,并在其上创建唯一索引,则该索引将默认具有过滤器。如果该属性是引用类型,则必须作为额外步骤指定它是可空的,可以使用流畅的API或属性来实现。我尚未使用属性进行测试,但是使用流畅的API,生成的索引将不会默认具有过滤器。考虑以下实体:
public class Thing
{
public int ThingId { get; set; }
public string Text { get; set; }
public int? StuffId { get; set; }
public Stuff Stuff { get; set; }
}
在以下的DbContext
中:
modelBuilder.Entity<Thing>(etb =>
{
etb.Property(t => t.Text)
.IsRequired(false);
etb.HasIndex(t => t.Text)
.IsUnique();
etb.HasIndex(t => t.StuffId)
.IsUnique();
});
StuffId
列生成的唯一索引默认会有过滤器,而Text
列上的索引则没有。必须明确指定Text
列索引的过滤器:
modelBuilder.Entity<Thing>(etb =>
{
etb.Property(t => t.Text)
.IsRequired(false);
etb.HasIndex(t => t.Text)
.HasFilter("Text IS NOT NULL")
.IsUnique();
etb.HasIndex(t => t.StuffId)
.IsUnique();
});
long?
的外键,并且在相应的列上生成的唯一索引确实有一个过滤器。我在上面的答案中显示的Label
属性是string
类型,因此必须通过使用etb.Property(r => r.Label).IsRequired(false)
使其可为空。该列上生成的唯一索引默认情况下没有过滤器,因此必须显式提供。 - jmcilhinney
SqlException: Cannot insert duplicate key row in object 'dbo.Employees' with unique index 'IX_Employees_FingerprintId'. The duplicate key value is (<NULL>).
- Lawrence