it-swarm.com.de

Eindeutige Schlüsseleinschränkungen für mehrere Spalten in Entity Framework

Ich verwende Entity Framework 5.0 Code First.

public class Entity
 {
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public string EntityId { get; set;}
   public int FirstColumn  { get; set;}
   public int SecondColumn  { get; set;}
 }

Ich möchte die Kombination zwischen FirstColumn und SecondColumn als eindeutig festlegen.

Beispiel:

Id  FirstColumn  SecondColumn 
1       1              1       = OK
2       2              1       = OK
3       3              3       = OK
5       3              1       = THIS OK 
4       3              3       = GRRRRR! HERE ERROR

Gibt es sowieso das zu tun?

213
Bassam Alugili

Mit Entity Framework 6.1 können Sie jetzt Folgendes tun:

[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }

[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }

Im zweiten Parameter des Attributs können Sie die Reihenfolge der Spalten im Index festlegen.
Weitere Informationen: MSDN

339
chuck

Ich habe drei Möglichkeiten gefunden, um das Problem zu lösen.

Eindeutige Indizes in EntityFramework Core:

Erster Ansatz:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Entity>()
   .HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}

Der zweite Ansatz zum Erstellen eindeutiger Abhängigkeiten mit EF Core mithilfe alternativer Schlüssel.

Beispiele

Eine Spalte:

modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");

Mehrere Spalten:

modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");

EF 6 und niedriger:


Erster Ansatz:

dbContext.Database.ExecuteSqlCommand(string.Format(
                        @"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})", 
                                 "Entitys", "FirstColumn, SecondColumn"));

Dieser Ansatz ist sehr schnell und nützlich, aber das Hauptproblem ist, dass Entity Framework nichts über diese Änderungen weiß!


Zweiter Ansatz:
Ich habe es in diesem Beitrag gefunden, aber ich habe es nicht selbst versucht.

CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
              true, "IX_Entitys");

Das Problem bei diesem Ansatz ist das Folgende: Es benötigt DbMigration. Was machen Sie also, wenn Sie es nicht haben?


Dritter Ansatz:
Ich denke, das ist das Beste, aber es braucht etwas Zeit, um es zu tun. Ich zeige Ihnen nur die Idee dahinter: In diesem Link http://code.msdn.Microsoft.com/CSASPNETUniqueConstraintInE-d357224a finden Sie den Code für die Annotation eindeutiger Schlüsseldaten:

[UniqueKey] // Unique Key 
public int FirstColumn  { get; set;}
[UniqueKey] // Unique Key 
public int SecondColumn  { get; set;}

// The problem hier
1, 1  = OK 
1 ,2  = NO OK 1 IS UNIQUE

Das Problem für diesen Ansatz; Wie kann ich sie kombinieren? Ich habe eine Idee, diese Microsoft-Implementierung zu erweitern, zum Beispiel:

[UniqueKey, 1] // Unique Key 
public int FirstColumn  { get; set;}
[UniqueKey ,1] // Unique Key 
public int SecondColumn  { get; set;}

Später im IDatabaseInitializer, wie im Microsoft-Beispiel beschrieben, können Sie die Schlüssel gemäß der angegebenen Ganzzahl kombinieren. Eines muss jedoch beachtet werden: Wenn die eindeutige Eigenschaft vom Typ string ist, müssen Sie die MaxLength festlegen.

102
Bassam Alugili

Wenn Sie Code-First verwenden, können Sie eine benutzerdefinierte Erweiterung implementieren HasUniqueIndexAnnotation

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration.Configuration;

internal static class TypeConfigurationExtensions
{
    public static PrimitivePropertyConfiguration HasUniqueIndexAnnotation(
        this PrimitivePropertyConfiguration property, 
        string indexName,
        int columnOrder)
    {
        var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = true };
        var indexAnnotation = new IndexAnnotation(indexAttribute);

        return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
    }
}

Dann benutze es wie folgt:

this.Property(t => t.Email)
    .HasColumnName("Email")
    .HasMaxLength(250)
    .IsRequired()
    .HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 0);

this.Property(t => t.ApplicationId)
    .HasColumnName("ApplicationId")
    .HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 1);

Was wird in dieser Migration führen:

public override void Up()
{
    CreateIndex("dbo.User", new[] { "Email", "ApplicationId" }, unique: true, name: "UQ_User_EmailPerApplication");
}

public override void Down()
{
    DropIndex("dbo.User", "UQ_User_EmailPerApplication");
}

Und landen schließlich in der Datenbank als:

CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
    [Email] ASC,
    [ApplicationId] ASC
)
71
niaher

Sie müssen einen zusammengesetzten Schlüssel definieren.

Mit Datenanmerkungen sieht es so aus:

public class Entity
 {
   public string EntityId { get; set;}
   [Key]
   [Column(Order=0)]
   public int FirstColumn  { get; set;}
   [Key]
   [Column(Order=1)]
   public int SecondColumn  { get; set;}
 }

Sie können dies auch mit modelBuilder tun, wenn Sie OnModelCreating überschreiben, indem Sie Folgendes angeben:

modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });
16
Admir Tuzović

Schließen Sie die @ Chuck-Antwort für die Verwendung von zusammengesetzte Indizes mit Fremdschlüssel ab.

Sie müssen eine Eigenschaft definieren, die den Wert des Fremdschlüssels enthält. Sie können diese Eigenschaft dann in der Indexdefinition verwenden.

Zum Beispiel haben wir eine Firma mit Mitarbeitern und nur wir haben eine eindeutige Einschränkung (Name, Firma) für jeden Mitarbeiter:

class Company
{
    public Guid Id { get; set; }
}

class Employee
{
    public Guid Id { get; set; }
    [Required]
    public String Name { get; set; }
    public Company Company  { get; set; }
    [Required]
    public Guid CompanyId { get; set; }
}

Nun das Mapping der Employee-Klasse:

class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap ()
    {
        ToTable("Employee");

        Property(p => p.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        Property(p => p.Name)
            .HasUniqueIndexAnnotation("UK_Employee_Name_Company", 0);
        Property(p => p.CompanyId )
            .HasUniqueIndexAnnotation("UK_Employee_Name_Company", 1);
        HasRequired(p => p.Company)
            .WithMany()
            .HasForeignKey(p => p.CompanyId)
            .WillCascadeOnDelete(false);
    }
}

Beachten Sie, dass ich auch die Erweiterung @niaher für eindeutige Indexanmerkungen verwendet habe.

8
Kryptos

Die Antwort von Niaher, die angibt, dass für die Verwendung der flüssigen API eine benutzerdefinierte Erweiterung erforderlich ist, war zum Zeitpunkt des Schreibens möglicherweise korrekt. Sie können die flüssige API jetzt (EF Core 2.1) wie folgt verwenden:

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2}).IsUnique();
5
GilShalit

Ich gehe davon aus, dass Sie immer möchten, dass EntityId der Primärschlüssel ist, daher ist das Ersetzen durch einen zusammengesetzten Schlüssel keine Option (schon deshalb, weil die Arbeit mit zusammengesetzten Schlüsseln sehr viel komplizierter ist nd weil Es ist nicht sehr sinnvoll, Primärschlüssel zu haben, die auch in der Geschäftslogik eine Bedeutung haben.

Das Mindeste, was Sie tun sollten, ist, einen eindeutigen Schlüssel für beide Felder in der Datenbank zu erstellen und beim Speichern von Änderungen auf Ausnahmen bezüglich der Verletzung eindeutiger Schlüssel zu prüfen.

Zusätzlich können (sollten) Sie vor dem Speichern von Änderungen nach eindeutigen Werten suchen. Der beste Weg, dies zu tun, ist eine Any() Abfrage, da sie die Menge der übertragenen Daten minimiert:

if (context.Entities.Any(e => e.FirstColumn == value1 
                           && e.SecondColumn == value2))
{
    // deal with duplicate values here.
}

Beachten Sie, dass dieser Scheck niemals ausreicht. Zwischen der Prüfung und dem eigentlichen Festschreiben besteht immer eine gewisse Latenz, sodass Sie immer die eindeutige Einschränkungs- und Ausnahmebehandlung benötigen.

2
Gert Arnold