使用Fluent API设置唯一约束?

194

我正在尝试使用Code First构建EF实体,并使用流畅的API创建EntityTypeConfiguration。创建主键很容易,但独特约束并非如此。我看到一些旧帖子建议使用本地SQL命令来执行此操作,但这似乎违背了初衷。在EF6中是否可以实现?

6个回答

297

EF6.2 中,您可以使用 HasIndex() 通过流畅的API为迁移添加索引。

https://github.com/aspnet/EntityFramework6/issues/274

示例

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

从EF6.1版本开始,你可以使用IndexAnnotation()在fluent API中为迁移添加索引。

http://msdn.microsoft.com/en-us/data/jj591617.aspx#PropertyIndex

你必须添加引用:

using System.Data.Entity.Infrastructure.Annotations;

基本示例

这是一个简单的用法示例,将索引添加到User.FirstName属性。

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));

实际例子:

这里有一个更加实际的例子。它在多个属性上添加了一个唯一索引User.FirstNameUser.LastName,并使用了一个索引名“IX_FirstNameLastName”。

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 1) { IsUnique = true }));

modelBuilder 
    .Entity<User>() 
    .Property(t => t.LastName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 2) { IsUnique = true }));

4
需要将列注释命名为“Index”!我写了另一个名称,但它无效!在我把它重命名回原来的“Index”之前,我花了几个小时,并且明白这很重要。:(框架中必须有一个常数来避免硬编码字符串。 - Alexander Vasilyev
10
常量被定义为 IndexAnnotation.AnnotationName - Nathan
3
@Nathan 谢谢!就是这样! 这篇帖子中的例子必须使用这个常数进行更正。 - Alexander Vasilyev
2
似乎在EF7 - DNX中找不到它。 - Shimmy Weitzhandler
2
我认为在第一个示例中创建IndexAttribute时,需要将IsUnique设置为true。像这样:new IndexAttribute() { IsUnique = true }。否则它只会创建普通(非唯一)索引。 - jakubka
显示剩余8条评论

137
作为对Yorro答案的补充,也可以通过使用属性来完成。
针对“int”类型的唯一键组合的示例:
[Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
public int UniqueKeyIntPart1 { get; set; }

[Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
public int UniqueKeyIntPart2 { get; set; }
如果数据类型是string,则必须添加MaxLength属性:
[Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
[MaxLength(50)]
public string UniqueKeyStringPart1 { get; set; }

[Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
[MaxLength(50)]
public string UniqueKeyStringPart2 { get; set; }

如果存在领域/存储模型分离的问题,可以考虑使用 Metadatatype 属性/类: https://msdn.microsoft.com/zh-cn/library/ff664465(v=pandp.50).aspx?f=255&MSPPError=-2147217396


一个快速的控制台应用程序示例:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;

namespace EFIndexTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new AppDbContext())
            {
                var newUser = new User { UniqueKeyIntPart1 = 1, UniqueKeyIntPart2 = 1, UniqueKeyStringPart1 = "A", UniqueKeyStringPart2 = "A" };
                context.UserSet.Add(newUser);
                context.SaveChanges();
            }
        }
    }

    [MetadataType(typeof(UserMetadata))]
    public class User
    {
        public int Id { get; set; }
        public int UniqueKeyIntPart1 { get; set; }
        public int UniqueKeyIntPart2 { get; set; }
        public string UniqueKeyStringPart1 { get; set; }
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class UserMetadata
    {
        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
        public int UniqueKeyIntPart1 { get; set; }

        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
        public int UniqueKeyIntPart2 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
        [MaxLength(50)]
        public string UniqueKeyStringPart1 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
        [MaxLength(50)]
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class AppDbContext : DbContext
    {
        public virtual DbSet<User> UserSet { get; set; }
    }
}

47
如果您想将域模型与存储问题完全分离,那么就不能这样做。 - Rickard Liljeberg
4
您需要确保引用了EntityFramework。 - MikeT
2
在EF7 - DNX中似乎找不到它。 - Shimmy Weitzhandler
2
代码不起作用,我编辑修复它被拒绝了。这是什么玩笑? - Cheesus Toast
4
只有在限制字符串长度的情况下才能使用此方法,因为SQL不允许将*nvarchar(max)*用作键。 - JMK
显示剩余5条评论

19

这里有一个用于更流畅地设置唯一索引的扩展方法:

public static class MappingExtensions
{
    public static PrimitivePropertyConfiguration IsUnique(this PrimitivePropertyConfiguration configuration)
    {
        return configuration.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsUnique = true }));
    }
}

使用方法:

modelBuilder 
    .Entity<Person>() 
    .Property(t => t.Name)
    .IsUnique();

将生成以下迁移:

public partial class Add_unique_index : DbMigration
{
    public override void Up()
    {
        CreateIndex("dbo.Person", "Name", unique: true);
    }

    public override void Down()
    {
        DropIndex("dbo.Person", new[] { "Name" });
    }
}

源地址:使用Entity Framework 6.1 fluent API创建唯一索引


17

@coni2k的回答是正确的,但你必须添加[StringLength]属性使其起作用,否则你将会收到一个无效键异常(下面是示例)。

[StringLength(65)]
[Index("IX_FirstNameLastName", 1, IsUnique = true)]
public string FirstName { get; set; }

[StringLength(65)]
[Index("IX_FirstNameLastName", 2, IsUnique = true)]
public string LastName { get; set; }

11

-1
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);

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