primary index framework constraint attribute entity-framework ef-code-first constraints multiple-columns unique-key

entity-framework - constraint - index attribute entity framework



Limitaciones clave únicas para varias columnas en Entity Framework (7)

Completar la respuesta de @chuck para usar índices compuestos con claves externas .

Debe definir una propiedad que contendrá el valor de la clave externa. A continuación, puede utilizar esta propiedad dentro de la definición del índice.

Por ejemplo, tenemos compañía con empleados y solo tenemos una restricción única en (nombre, compañía) para cualquier empleado:

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

Ahora el mapeo de la clase Employee:

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

Tenga en cuenta que también usé la extensión @niaher para una anotación de índice única.

Estoy usando el código Entity Framework 5.0 primero;

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

Quiero hacer la combinación entre FirstColumn y SecondColumn como única.

Ejemplo:

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

¿Hay alguna forma de hacer eso?


Con Entity Framework 6.1, ahora puedes hacer esto:

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

El segundo parámetro en el atributo es donde puede especificar el orden de las columnas en el índice.
Más información: MSDN


Encontré tres maneras de resolver el problema.

Índices únicos en EntityFramework Core:

Primer enfoque:

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

El segundo enfoque para crear restricciones únicas con EF Core mediante el uso de claves alternativas.

Ejemplos

Una columna:

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

Múltiples columnas:

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

EF 6 y por debajo:

Primer enfoque:

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

Este enfoque es muy rápido y útil, pero el problema principal es que Entity Framework no sabe nada acerca de esos cambios.

Segundo enfoque:
Lo encontré en este post pero no lo probé por mi cuenta.

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

El problema de este enfoque es el siguiente: necesita DbMigration, entonces, ¿qué hace si no lo tiene?

Tercer enfoque:
Creo que este es el mejor, pero requiere algo de tiempo para hacerlo. Solo le mostraré la idea detrás de esto: en este enlace http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a puede encontrar el código para la anotación de datos de clave única:

[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

El problema para este enfoque; ¿Cómo puedo combinarlos? Tengo una idea para extender esta implementación de Microsoft por ejemplo:

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

Más adelante en el IDatabaseInitializer como se describe en el ejemplo de Microsoft, puede combinar las claves de acuerdo con el número entero dado. Sin embargo, hay que tener en cuenta una cosa: si la propiedad única es de tipo cadena, entonces debe establecer MaxLength.


Es necesario definir una clave compuesta.

Con las anotaciones de datos se ve así:

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

También puede hacer esto con modelBuilder al anular OnModelCreating especificando:

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


La respuesta de niaher que indica que para usar la API fluida que necesita una extensión personalizada puede haber sido correcta en el momento de la escritura. Ahora puede (EF Core 2.1) usar la API fluida de la siguiente manera:

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


Supongo que siempre desea que EntityId sea ​​la clave principal, por lo que reemplazarla con una clave compuesta no es una opción (solo porque las claves compuestas son mucho más complicadas de trabajar y porque no es muy sensato tener claves primarias que también tengan significado en la lógica de negocios).

Lo menos que debe hacer es crear una clave única en ambos campos de la base de datos y verificar específicamente las excepciones de violación de clave única al guardar los cambios.

Además, podría (debería) verificar valores únicos antes de guardar los cambios. La mejor manera de hacerlo es mediante una consulta Any() , porque minimiza la cantidad de datos transferidos:

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

Tenga en cuenta que esta comprobación por sí sola nunca es suficiente. Siempre hay algo de latencia entre la verificación y el compromiso real, por lo que siempre necesitará la restricción única + el manejo de excepciones.


Si está utilizando Code-First , puede implementar una extensión personalizada 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); } }

Entonces utilízalo así:

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

Lo que dará lugar a esta migración:

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

Y eventualmente terminará en la base de datos como:

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