EF Core允许唯一列包含多个空值吗?

17

我的实体拥有一个允许为空的属性。但是,如果它不为空,则必须是唯一的。换句话说,这个列是唯一的,但允许多个空值。

我已经尝试过:

config.Property(p => p.ProductId).IsRequired(false);

我记得在Core EF之前,我曾经苦苦挣扎地让它正常工作。

这个可能吗?我该如何配置实体?

2个回答

27

4
我假设这是针对SQL Server的 - 你知道它是否也被SQLite支持吗? "Filtered index"似乎是微软的东西? - grokky
我使用的是EF Core 1.0.2,但这似乎对我不起作用? modelBuilder.Entity("DataModel.Models.Employee", b =>{b.HasIndex("FingerprintId").IsUnique();});仍然出现SqlException: Cannot insert duplicate key row in object 'dbo.Employees' with unique index 'IX_Employees_FingerprintId'. The duplicate key value is (<NULL>). - Lawrence
@ grokky,您介意分享一下您正在编写该部分代码的确切部分吗?总的来说,我对EF还很陌生,我已经看到多个位置具有与此答案类似的语法。 - Lawrence
@Lawrence 这取决于你如何设计你的系统。Erik的建议绝对可行。我建议你开一个新问题,展示你的EF配置代码。也许Erik可以看一下,他是EF大师之一。 - grokky

3

我知道这篇文章有点旧了,但是对于那些现在才发现它的人来说,我在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();
                          });

记录一下,被接受的答案是正确的(但对于EF 5来说不完整)。当索引是唯一的时,EF会创建一个过滤索引。 - Gert Arnold
@GertArnold,我想我已经找出了差异所在。我有一个类型为long?的外键,并且在相应的列上生成的唯一索引确实有一个过滤器。我在上面的答案中显示的Label属性是string类型,因此必须通过使用etb.Property(r => r.Label).IsRequired(false)使其可为空。该列上生成的唯一索引默认情况下没有过滤器,因此必须显式提供。 - jmcilhinney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接