Mengatur Batasan unik dengan API yang lancar?

185

Saya mencoba membangun Entitas EF dengan Code First, dan EntityTypeConfigurationmenggunakan API yang lancar. membuat kunci utama itu mudah tetapi tidak demikian dengan Batasan Unik. Saya melihat posting lama yang menyarankan untuk mengeksekusi perintah SQL asli untuk ini, tapi itu tampaknya mengalahkan tujuannya. apakah ini mungkin dengan EF6?

kob490
sumber

Jawaban:

275

Pada EF6.2 , Anda dapat menggunakan HasIndex()untuk menambahkan indeks untuk migrasi melalui API yang lancar.

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

Contoh

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

Pada EF6.1 dan seterusnya, Anda dapat menggunakan IndexAnnotation()untuk menambahkan indeks untuk migrasi di API lancar Anda.

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

Anda harus menambahkan referensi ke:

using System.Data.Entity.Infrastructure.Annotations;

Contoh dasar

Ini adalah penggunaan sederhana, menambahkan indeks pada User.FirstNameproperti

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

Contoh Praktis:

Ini adalah contoh yang lebih realistis. Itu menambahkan indeks unik pada banyak properti: User.FirstNamedan User.LastName, dengan nama indeks "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 }));
Yorro
sumber
4
Ini diperlukan untuk memberi nama anotasi kolom sebagai "Indeks"! Saya menulis nama lain dan itu tidak berhasil! Saya menghabiskan berjam-jam sebelum saya mencoba mengubah namanya menjadi "Indeks" asli seperti pada posting Anda dan memahami bahwa ini penting. :( Harus ada konstanta untuk itu dalam rangka untuk tidak menyulitkan kode string.
Alexander Vasilyev
10
@AlexanderVasilyev Konstanta didefinisikan sebagaiIndexAnnotation.AnnotationName
Nathan
3
@Nathan Terima kasih! Itu dia! Contoh dalam posting ini harus diperbaiki menggunakan konstanta ini.
Alexander Vasilyev
2
Tampaknya tidak dapat menemukannya di EF7 - DNX
Shimmy Weitzhandler
2
Saya percaya bahwa IsUnique perlu disetel ke true ketika membuat IndexAttribute pada contoh pertama. Seperti ini: new IndexAttribute() { IsUnique = true }. Jika tidak, ia hanya akan membuat indeks biasa (tidak unik).
jakubka
134

Sebagai tambahan untuk jawaban Yorro, itu juga bisa dilakukan dengan menggunakan atribut.

Sampel untuk intjenis kombinasi tombol unik:

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

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

Jika tipe data adalah string, maka MaxLengthatribut harus ditambahkan:

[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; }

Jika ada masalah pemisahan model domain / penyimpanan, menggunakan Metadatatypeatribut / kelas dapat menjadi pilihan: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2147217396


Contoh aplikasi konsol cepat:

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; }
    }
}
coni2k
sumber
45
Tidak jika Anda ingin menjaga model domain Anda sepenuhnya terpisah dari masalah penyimpanan.
Rickard Liljeberg
4
Anda juga perlu memastikan Anda memiliki referensi ke EntityFramework
Michael Tranchida
2
Bersikaplah baik jika atribut Indeks dipisahkan dari Kerangka Entitas sehingga saya dapat memasukkannya dalam proyek Model saya. Saya mengerti bahwa ini adalah masalah penyimpanan, tetapi alasan utama saya menggunakannya adalah untuk menempatkan batasan unik pada hal-hal seperti Nama Pengguna dan Nama Peran.
Sam
2
Tampaknya tidak dapat menemukannya di EF7 - DNX
Shimmy Weitzhandler
4
Ini hanya berfungsi jika Anda juga membatasi panjang string, karena SQL tidak mengizinkan nvarchar (maks) digunakan sebagai kunci.
JMK
17

Berikut adalah metode ekstensi untuk mengatur indeks unik lebih lancar:

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

Pemakaian:

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

Akan menghasilkan migrasi seperti:

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" });
    }
}

Src: Membuat Indeks Unik dengan Kerangka Entitas 6.1 API lancar

Bartho Bernsmann
sumber
16

@ coni2k jawaban benar tetapi Anda harus menambahkan [StringLength]atribut agar berfungsi jika tidak Anda akan mendapatkan pengecualian kunci yang tidak valid (Contoh di bawah).

[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; }
Arijoon
sumber
0
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);
Rousonur Jaman
sumber