Código de Entity Framework primera columna única

114

Estoy usando Entity Framework 4.3 y usando Code Fist.

Tengo una clase

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

¿Cómo le digo a Entity Framework que UserName tiene que ser único al crear la tabla de base de datos? Preferiría usar anotaciones de datos en lugar del archivo de configuración si es posible.

cpoDesign
fuente

Respuestas:

257

En Entity Framework 6.1+, puede usar este atributo en su modelo:

[Index(IsUnique=true)]

Puede encontrarlo en este espacio de nombres:

using System.ComponentModel.DataAnnotations.Schema;

Si el campo de su modelo es una cadena, asegúrese de que no esté configurado en nvarchar (MAX) en SQL Server o verá este error con Entity Framework Code First:

La columna 'x' de la tabla 'dbo.y' es de un tipo que no es válido para su uso como columna clave en un índice.

La razón es por esto:

SQL Server conserva el límite de 900 bytes para el tamaño total máximo de todas las columnas de clave de índice ".

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

Puede resolver esto estableciendo una longitud máxima de cadena en su modelo:

[StringLength(450)]

Su modelo se verá así ahora en EF CF 6.1+:

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

Actualizar:

si usa Fluent:

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

y utilizar en su modelBuilder:

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

Actualización 2

para EntityFrameworkCore, consulte también este tema: https://github.com/aspnet/EntityFrameworkCore/issues/1698

Actualización 3

para EF6.2, consulte: https://github.com/aspnet/EntityFramework6/issues/274

Actualización 4

ASP.NET Core Mvc 2.2 con EF Core:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Unique { get; set; }
juFo
fuente
23
¡Gracias por responder a la publicación anterior de este chico con información actual relevante!
Jim Yarbro
3
¿Por qué un índice, por qué no solo una restricción?
John Henckel
2
Para responder a la pregunta de índice vs "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."
restricción
3
Nota; en Entity Framework Core 1.0.0-preview2-final, un método de anotaciones de datos no está disponible - docs.efproject.net/en/latest/modeling/…
Edward Comeau
26

EF no admite columnas únicas excepto claves. Si está utilizando EF Migraciones, puede obligar a EF a crear un índice único en la UserNamecolumna (en el código de migración, no mediante ninguna anotación) pero la unicidad se aplicará solo en la base de datos. Si intenta guardar un valor duplicado, tendrá que detectar la excepción (violación de restricción) activada por la base de datos.

Ladislav Mrnka
fuente
1
¡Eso es exactamente lo que estaba buscando! Me preguntaba si era posible agregar tales restricciones mediante migraciones.
Alex Jorgenson
@Ladislav Mrnka: ¿Esto también es posible a través del método Seed?
Raheel Khan
2
¿Le importaría dar un ejemplo? ¡Gracias!
Zero3
10

A partir de su código, resulta evidente que usa POCO. No es necesario tener otra clave: puede agregar un índice como lo sugiere juFo .
Si usa Fluent API en lugar de atribuir la propiedad UserName, la anotación de su columna debería verse así:

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

Esto creará el siguiente script SQL:

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]

Si intenta insertar varios usuarios con el mismo nombre de usuario, obtendrá una excepción DbUpdateException con el siguiente mensaje:

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

Nuevamente, las anotaciones de columna no están disponibles en Entity Framework antes de la versión 6.1.

Alexander Christov
fuente
¿Qué pasa con la creación de un índice con varias columnas usando la configuración?
FindOutIslamNow
Uno está en UserName, ¿cuál es el otro?
Alexander Christov
5

Tenga en cuenta que en Entity Framework 6.1 (actualmente en versión beta) admitirá IndexAttribute para anotar las propiedades del índice, lo que dará como resultado automáticamente un índice (único) en sus migraciones de Code First.

Robba
fuente
2

En EF 6.2 usando FluentAPI , puede usarHasIndex()

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

Solución para EF4.3

Nombre de usuario único

Agregue anotación de datos sobre la columna como:

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

Identificación única , agregué decoración [Clave] sobre mi columna y listo. Misma solución que se describe aquí: https://msdn.microsoft.com/en-gb/data/jj591583.aspx

ES DECIR:

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

Respuestas alternativas

usando la anotación de datos

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

usando mapeo

  mb.Entity<User>()
            .HasKey(i => i.UserId);
        mb.User<User>()
          .Property(i => i.UserId)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
          .HasColumnName("UserId");
cpoDesign
fuente
25
Tenga cuidado con esta solución. Agregar el Keyatributo a la UserNamepropiedad hará que la UserNamepropiedad se convierta en la clave principal en la base de datos. Solo la UserIdpropiedad debe estar marcada con el Keyatributo. Esta solución le dará el comportamiento 'correcto' en el lado de la programación y le dará un diseño de base de datos incorrecto.
Alex Jorgenson