Entity Framework代码优先惟一列

148

我正在使用Entity Framework 4.3并使用Code Fist。

我有一个类

public class User
{
   public int UserId{get;set;}
   public string UserName{get;set;}
}

如何告诉Entity Framework在创建数据库表时,UserName必须是唯一的?如果可能的话,我更愿意使用数据注释而不是配置文件。

6个回答

313
在Entity Framework 6.1+中,你可以在你的模型上使用这个属性:
[Index(IsUnique=true)]

你可以在这个命名空间中找到它:

using System.ComponentModel.DataAnnotations.Schema;

如果您的模型字段是字符串,请确保它在 SQL Server 中未设置为 nvarchar(MAX),否则您将在使用 Entity Framework Code First 时看到此错误:

表 'dbo.y' 中的列 'x' 属于一种无法用作索引中键列的类型。

原因是这样的:

SQL Server 为所有索引键列的最大总大小保留了 900 字节的限制。

(来源:http://msdn.microsoft.com/en-us/library/ms191241.aspx)

您可以通过在模型上设置最大字符串长度来解决此问题:

[StringLength(450)]

现在在EF CF 6.1+中,您的模型将看起来像这样:

public class User
{
   public int UserId{get;set;}
   [StringLength(450)]
   [Index(IsUnique=true)]
   public string UserName{get;set;}
}

更新:

如果您使用Fluent:

  public class UserMap : EntityTypeConfiguration<User>
  {
    public UserMap()
    {
      // ....
      Property(x => x.Name).IsRequired().HasMaxLength(450).HasColumnAnnotation("Index", new IndexAnnotation(new[] { new IndexAttribute("Index") { IsUnique = true } }));
    }
  }

并在您的modelBuilder中使用:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  // ...
  modelBuilder.Configurations.Add(new UserMap());
  // ...
}

更新2

有关EntityFrameworkCore,请参阅此主题:https://github.com/aspnet/EntityFrameworkCore/issues/1698

更新3

有关EF6.2,请参见:https://github.com/aspnet/EntityFramework6/issues/274

更新4

ASP.NET Core Mvc 2.2 with EF Core:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Unique { get; set; }

29
感谢您回复这位先生的旧帖,并提供了一些相关的最新信息! - Jim Yarbro
3
为什么要使用索引,而不只是使用约束条件呢? - John Henckel
3
回答索引与约束的问题...MSDN: "创建唯一约束和创建独立于约束的唯一索引之间没有显着差异。数据验证以相同方式进行,查询优化器不区分由约束或手动创建的唯一索引。但是,在列上创建唯一约束可以清晰地表明索引的目的。" https://msdn.microsoft.com/zh-cn/library/ms187019.aspx - tinonetic
3
注意:在Entity Framework Core 1.0.0-preview2-final中,数据注释方法不可用。请参考https://docs.efproject.net/en/latest/modeling/indexes.html#data-annotations。 - Edward Comeau
如果表已经有值,我不能将其设置为null或默认值。当我生成迁移脚本时,添加约束的脚本会抛出错误。 - Arjun Menon
4
在EntityFrameworkCore中,可以通过以下方式完成:[Index(propertyNames: nameof(UserName), IsUnique = true)] public class User { public int UserId{get;set;} [StringLength(450)] public string UserName{get;set;} } - Patrick Koorevaar

29

EF不支持除键以外的唯一列。如果您正在使用EF迁移,可以强制EF在UserName列上创建唯一索引(在迁移代码中,而不是通过任何注释),但唯一性仅在数据库中强制执行。如果您尝试保存重复值,您将不得不捕获数据库引发的异常(约束违规)。


1
这正是我正在寻找的!我在想是否可以使用迁移来添加这样的约束。 - Alex Jorgenson
@Ladislav Mrnka:这也可以通过Seed方法实现吗? - Raheel Khan
3
可以给一个例子吗?谢谢! - Zero3

20

在EF 6.2中使用FluentAPI,您可以使用HasIndex()

modelBuilder.Entity<User>().HasIndex(u => u.UserName).IsUnique();

11

从您的代码中可以看出您正在使用POCO,因此另一个键是不必要的:您可以按照juFo的建议添加索引。
如果您使用Fluent API而不是属性化UserName属性,则您的列注释应如下所示:

this.Property(p => p.UserName)
    .HasColumnAnnotation("Index", new IndexAnnotation(new[] { 
        new IndexAttribute("Index") { IsUnique = true } 
    }
));

这将创建以下的 SQL 脚本:

CREATE UNIQUE NONCLUSTERED INDEX [Index] ON [dbo].[Users]
(
    [UserName] ASC
)
WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, 
    IGNORE_DUP_KEY = OFF, 
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]

如果您尝试插入多个具有相同UserName的用户,将会产生一个带有以下信息的DbUpdateException:

Cannot insert duplicate key row in object 'dbo.Users' with unique index 'Index'. 
The duplicate key value is (...).
The statement has been terminated.

再次强调,在Entity Framework 6.1之前的版本中,列注释是不可用的。


使用配置创建一个包含多列的索引怎么样? - FindOutIslamNow
一个是关于用户名的,另一个是什么? - Alexander Christov

5
请注意,在Entity Framework 6.1(目前处于测试版)中,将支持IndexAttribute来注释索引属性,这将自动在您的Code First Migrations中生成(唯一)索引。

-11

EF4.3的解决方案

唯一用户名

在列上添加数据注释:

 [Index(IsUnique = true)]
 [MaxLength(255)] // for code-first implementations
 public string UserName{get;set;}

唯一标识符,我已经在我的列上添加了装饰 [Key] 并完成了操作。与此处描述的解决方案相同:https://msdn.microsoft.com/en-gb/data/jj591583.aspx

例如:

[Key]
public int UserId{get;set;}

备选答案

使用数据注释

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("UserId")]

使用映射。
  mb.Entity<User>()
            .HasKey(i => i.UserId);
        mb.User<User>()
          .Property(i => i.UserId)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
          .HasColumnName("UserId");

29
请谨慎使用这个解决方案。将Key属性添加到UserName属性会导致UserName属性成为数据库中的主键。只有UserId属性应该被标记为Key属性。这个解决方案将在编程端提供“正确”的行为,但会给你错误的数据库设计。 - Alex Jorgenson

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