Festlegen einer eindeutigen Einschränkung mit einer fließenden API?

184

Ich versuche, eine EF-Entität mit Code First und einer EntityTypeConfigurationfließenden API zu erstellen . Das Erstellen von Primärschlüsseln ist einfach, mit einer eindeutigen Einschränkung jedoch nicht. Ich habe alte Beiträge gesehen, in denen vorgeschlagen wurde, native SQL-Befehle auszuführen, aber die den Zweck zu vereiteln scheinen. ist das mit EF6 möglich?

kob490
quelle

Antworten:

274

In EF6.2 können Sie HasIndex()Indizes für die Migration über eine flüssige API hinzufügen.

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

Beispiel

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

Auf EF6.1 ab, können Sie IndexAnnotation()Indizes in Ihrer fließend API für Migration hinzuzufügen.

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

Sie müssen einen Verweis hinzufügen auf:

using System.Data.Entity.Infrastructure.Annotations;

Grundlegendes Beispiel

Hier ist eine einfache Verwendung, bei der ein Index für die User.FirstNameEigenschaft hinzugefügt wird

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

Praktisches Beispiel:

Hier ist ein realistischeres Beispiel. Es fügt einen eindeutigen Index für mehrere Eigenschaften hinzu: User.FirstNameund User.LastNamemit dem Indexnamen "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
quelle
4
Dies ist erforderlich, um die Spaltenanmerkung als "Index" zu benennen! Ich habe einen anderen Namen geschrieben und es hat nicht funktioniert! Ich habe Stunden damit verbracht, es wie in Ihrem Beitrag in den ursprünglichen "Index" umzubenennen, und habe verstanden, dass dies wichtig ist. :( Es muss eine Konstante im Framework geben, damit die Zeichenfolge nicht hart codiert wird.
Alexander Vasilyev
10
@ AlexanderVasilyev Die Konstante ist definiert alsIndexAnnotation.AnnotationName
Nathan
3
@ Nathan Danke! Das ist es! Das Beispiel in diesem Beitrag muss mit dieser Konstante korrigiert werden.
Alexander Vasilyev
2
Kann nicht scheinen, es in EF7 - DNX
Shimmy Weitzhandler
2
Ich glaube, dass IsUnique beim Erstellen von IndexAttribute im ersten Beispiel auf true gesetzt werden muss. So : new IndexAttribute() { IsUnique = true }. Andernfalls wird nur ein regulärer (nicht eindeutiger) Index erstellt.
Jakubka
134

Als Ergänzung zu Yorros Antwort kann dies auch mithilfe von Attributen erfolgen.

Beispiel für eine inteindeutige Tastenkombination:

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

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

Wenn der Datentyp ist string, MaxLengthmuss das Attribut hinzugefügt werden:

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

Wenn ein Problem mit der Trennung von Domäne und Speichermodell besteht, kann die Verwendung von MetadatatypeAttribut / Klasse eine Option sein: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2147217396


Ein schnelles Beispiel für eine Konsolen-App:

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
quelle
45
Nicht, wenn Sie Ihr Domain-Modell vollständig von Speicherproblemen trennen möchten.
Rickard Liljeberg
4
Sie müssen auch sicherstellen, dass Sie einen Verweis auf EntityFramework
Michael Tranchida
2
Seien Sie nett, wenn das Index-Attribut vom Entity Framework getrennt wurde, damit ich es in mein Models-Projekt aufnehmen kann. Ich verstehe, dass es sich um ein Speicherproblem handelt, aber der Hauptgrund, warum ich es verwende, besteht darin, Dinge wie Benutzernamen und Rollennamen eindeutig einzuschränken.
Sam
2
Kann nicht scheinen, es in EF7 - DNX
Shimmy Weitzhandler
4
Dies funktioniert nur, wenn Sie auch die Länge der Zeichenfolge einschränken, da SQL nicht zulässt, dass nvarchar (max) als Schlüssel verwendet wird.
JMK
17

Hier ist eine Erweiterungsmethode zum flüssigeren Festlegen eindeutiger Indizes:

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

Verwendung:

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

Erzeugt eine Migration wie:

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: Erstellen eines eindeutigen Index mit der fließenden API von Entity Framework 6.1

Bartho Bernsmann
quelle
16

Die Antwort von @ coni2k ist korrekt. Sie müssen jedoch ein [StringLength]Attribut hinzufügen , damit es funktioniert. Andernfalls wird eine ungültige Schlüsselausnahme angezeigt (Beispiel unten).

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