Entity Framework-Code erste eindeutige Spalte

113

Ich verwende Entity Framework 4.3 und Code Fist.

ich habe ein klasse

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

Wie kann ich Entity Framework mitteilen, dass Benutzername beim Erstellen einer Datenbanktabelle eindeutig sein muss? Ich würde es vorziehen, wenn möglich Datenanotationen anstelle der Konfigurationsdatei zu verwenden.

cpoDesign
quelle

Antworten:

256

In Entity Framework 6.1+ können Sie dieses Attribut für Ihr Modell verwenden:

[Index(IsUnique=true)]

Sie finden es in diesem Namespace:

using System.ComponentModel.DataAnnotations.Schema;

Wenn Ihr Modellfeld eine Zeichenfolge ist, stellen Sie sicher, dass es in SQL Server nicht auf nvarchar (MAX) festgelegt ist. Andernfalls wird dieser Fehler zuerst mit Entity Framework-Code angezeigt:

Die Spalte 'x' in der Tabelle 'dbo.y' ist vom Typ, der für die Verwendung als Schlüsselspalte in einem Index ungültig ist.

Der Grund ist aus diesem Grund:

SQL Server behält das 900-Byte-Limit für die maximale Gesamtgröße aller Indexschlüsselspalten bei. "

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

Sie können dies lösen, indem Sie eine maximale Zeichenfolgenlänge für Ihr Modell festlegen:

[StringLength(450)]

Ihr Modell wird jetzt in EF CF 6.1+ so aussehen:

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

Aktualisieren:

Wenn Sie Fluent verwenden:

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

und verwenden Sie in Ihrem modelBuilder:

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

Update 2

Informationen zu EntityFrameworkCore finden Sie auch in diesem Thema: https://github.com/aspnet/EntityFrameworkCore/issues/1698

Update 3

EF6.2 finden Sie unter: https://github.com/aspnet/EntityFramework6/issues/274

Update 4

ASP.NET Core Mvc 2.2 mit EF Core:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Unique { get; set; }
juFo
quelle
23
Vielen Dank, dass Sie auf den alten Beitrag dieses Typen mit einigen relevanten aktuellen Informationen geantwortet haben!
Jim Yarbro
3
Warum ein Index, warum nicht nur eine Einschränkung?
John Henckel
2
Um die Frage Index gegen Kontraint zu beantworten ... MSDN: "There are no significant differences between creating a UNIQUE constraint and creating a unique index that is independent of a constraint. Data validation occurs in the same manner, and the query optimizer does not differentiate between a unique index created by a constraint or manually created. However, creating a UNIQUE constraint on the column makes the objective of the index clear." msdn.microsoft.com/en-us/library/ms187019.aspx
user919426
3
Hinweis; In Entity Framework Core 1.0.0-Preview2-Final ist keine Methode für Datenanmerkungen
Edward Comeau
26

EF unterstützt keine eindeutigen Spalten außer Schlüsseln. Wenn Sie EF-Migrationen verwenden, können Sie EF zwingen, einen eindeutigen Index für die UserNameSpalte zu erstellen (im Migrationscode, nicht durch Anmerkungen). Die Eindeutigkeit wird jedoch nur in der Datenbank erzwungen. Wenn Sie versuchen, einen doppelten Wert zu speichern, müssen Sie eine von der Datenbank ausgelöste Ausnahme (Einschränkungsverletzung) abfangen.

Ladislav Mrnka
quelle
1
Genau das habe ich gesucht! Ich habe mich gefragt, ob es möglich ist, solche Einschränkungen mithilfe von Migrationen hinzuzufügen.
Alex Jorgenson
@Ladislav Mrnka: Ist das auch über die Seed-Methode möglich?
Raheel Khan
2
Möchten Sie ein Beispiel geben? Vielen Dank!
Zero3
10

Aus Ihrem Code geht hervor, dass Sie POCO verwenden. Ein weiterer Schlüssel ist nicht erforderlich : Sie können einen Index hinzufügen, wie von juFo vorgeschlagen .
Wenn Sie die Fluent-API verwenden, anstatt die UserName-Eigenschaft zuzuweisen, sollte Ihre Spaltenanmerkung folgendermaßen aussehen:

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

Dadurch wird das folgende SQL-Skript erstellt:

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]

Wenn Sie versuchen, mehrere Benutzer mit demselben Benutzernamen einzufügen, wird eine DbUpdateException mit der folgenden Meldung angezeigt:

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

Auch hier sind Spaltenanmerkungen in Entity Framework vor Version 6.1 nicht verfügbar.

Alexander Christov
quelle
Was ist mit dem Erstellen eines Index mit mehreren Spalten mithilfe der Konfiguration?
FindOutIslamNow
Einer ist auf Benutzername, was ist der andere?
Alexander Christov
5

Beachten Sie, dass in Entity Framework 6.1 (derzeit in der Beta) das IndexAttribute unterstützt wird, um die Indexeigenschaften zu kommentieren, was automatisch zu einem (eindeutigen) Index in Ihren Code First Migrations führt.

Robba
quelle
2

In EF 6.2 mit FluentAPI können Sie verwendenHasIndex()

modelBuilder.Entity<User>().HasIndex(u => u.UserName).IsUnique();
Anas Alweish
quelle
-13

Lösung für EF4.3

Eindeutiger Benutzername

Fügen Sie Datenanmerkungen über der Spalte hinzu als:

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

Eindeutige ID , ich habe Dekoration [Schlüssel] über meiner Spalte hinzugefügt und fertig. Gleiche Lösung wie hier beschrieben: https://msdn.microsoft.com/en-gb/data/jj591583.aspx

IE:

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

Alternative Antworten

Verwenden von Datenanmerkungen

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

mit Mapping

  mb.Entity<User>()
            .HasKey(i => i.UserId);
        mb.User<User>()
          .Property(i => i.UserId)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
          .HasColumnName("UserId");
cpoDesign
quelle
25
Seien Sie vorsichtig mit dieser Lösung. Durch Hinzufügen des KeyAttributs zur UserNameEigenschaft wird die UserNameEigenschaft zum Primärschlüssel in der Datenbank. Nur die UserIdEigenschaft sollte mit dem KeyAttribut markiert werden. Diese Lösung gibt Ihnen das "richtige" Verhalten auf der Programmierseite, während Sie ein falsches Datenbankdesign erhalten.
Alex Jorgenson