Pregunta
¿Es posible definir una restricción única en una propiedad usando la sintaxis fluida o un atributo? Si no, ¿cuáles son las soluciones?
Tengo una clase de usuario con una clave principal, pero me gustaría asegurarme de que la dirección de correo electrónico también sea única. ¿Es esto posible sin editar la base de datos directamente?
Solución (basada en la respuesta de Matt)
public class MyContext : DbContext {
public DbSet<User> Users { get; set; }
public override int SaveChanges() {
foreach (var item in ChangeTracker.Entries<IModel>())
item.Entity.Modified = DateTime.Now;
return base.SaveChanges();
}
public class Initializer : IDatabaseInitializer<MyContext> {
public void InitializeDatabase(MyContext context) {
if (context.Database.Exists() && !context.Database.CompatibleWithModel(false))
context.Database.Delete();
if (!context.Database.Exists()) {
context.Database.Create();
context.Database.ExecuteSqlCommand("alter table Users add constraint UniqueUserEmail unique (Email)");
}
}
}
}
ObjectContext
oDbContext
.Respuestas:
Por lo que puedo decir, no hay forma de hacer esto con Entity Framework en este momento. Sin embargo, esto no es solo un problema con restricciones únicas ... es posible que desee crear índices, verificar restricciones y posiblemente disparadores y otras construcciones también. Aquí hay un patrón simple que puede usar con su configuración de código primero, aunque es cierto que no es independiente de la base de datos:
Otra opción es si su modelo de dominio es el único método para insertar / actualizar datos en su base de datos, podría implementar el requisito de exclusividad usted mismo y dejar la base de datos fuera de él. Esta es una solución más portátil y lo obliga a ser claro acerca de las reglas de su negocio en su código, pero deja su base de datos abierta a datos no válidos que se repasan.
fuente
SaveChanges()
), pero aún existe la posibilidad de que otra inserción / actualización se deslice entre el momento de la verificación de la unicidad y el momento deSaveChanges()
. Entonces, dependiendo de cuán crítica sea la misión de la aplicación y la probabilidad de una violación de unicidad, probablemente sea mejor agregar la restricción a la base de datos.serializable isolation level
(o bloqueo de tabla personalizado, ugh) realmente le permitiría garantizar la unicidad en su código. Pero la mayoría de las personas no lo usanserializable isolation level
por razones de rendimiento. El valor predeterminado en MS Sql Server esread committed
. Vea la serie de 4 partes a partir de: michaeljswart.com/2010/03/…Comenzando con EF 6.1 ahora es posible:
Esto le dará un índice único en lugar de una restricción única, estrictamente hablando. Para la mayoría de los propósitos prácticos son lo mismo .
fuente
No está realmente relacionado con esto, pero podría ayudar en algunos casos.
Si está buscando crear un índice compuesto único en, digamos, 2 columnas que actuarán como una restricción para su tabla, a partir de la versión 4.3 puede usar el nuevo mecanismo de migraciones para lograrlo:
Básicamente, debe insertar una llamada como esta en uno de sus scripts de migración:
Algo como eso:
fuente
DropIndex("TableName", new[] { "Column1", "Column2" });
Hago un truco completo para ejecutar SQL cuando se crea la base de datos. Creo mi propio DatabaseInitializer y heredo de uno de los inicializadores proporcionados.
Ese es el único lugar que pude encontrar en mis declaraciones SQL.
Esto es de CTP4. No sé cómo funciona en CTP5.
fuente
Solo tratando de averiguar si había una manera de hacer esto, solo la forma en que descubrí que hasta ahora lo estaba aplicando, creé un atributo para agregar a cada clase donde proporcionas el nombre de los campos que necesitas para ser único:
Luego en mi clase lo agregaré:
Finalmente, agregaré un método en mi repositorio, en el método Agregar o al guardar cambios como este:
No es muy agradable, ya que necesitamos confiar en la reflexión, ¡pero este es el enfoque que funciona para mí! = D
fuente
También en 6.1 puedes usar la versión de sintaxis fluida de la respuesta de @ mihkelmuur de esta manera:
El método fluido no es IMO perfecto, pero al menos es posible ahora.
Más detalles en el blog de Arthur Vickers http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
fuente
Una manera fácil en visual basic usando EF5 Code First Migrations
Muestra de clase pública
Clase final
El atributo MaxLength es muy importante para un índice único del tipo de cadena
Ejecute cmd: update-database -verbose
después de ejecutar cmd: agregar-migración 1
en el archivo generado
fuente
Similar a la respuesta de Tobias Schittkowski pero C # y tiene la capacidad de tener múltiples campos en los constrtaints.
Para usar esto, simplemente coloque un [Único] en cualquier campo que desee que sea único. Para las cadenas, tendrá que hacer algo como (tenga en cuenta el atributo MaxLength):
porque el campo de cadena predeterminado es nvarchar (max) y eso no se permitirá en una clave.
Para múltiples campos en la restricción puede hacer:
Primero, el atributo único:
Luego, incluya una extensión útil para obtener el nombre de la tabla de la base de datos de un tipo:
Luego, el inicializador de la base de datos:
fuente
Resolví el problema por reflexión (lo siento, amigos, VB.Net ...)
Primero, defina un atributo UniqueAttribute:
Luego, mejora tu modelo como
Finalmente, cree un DatabaseInitializer personalizado (en mi versión, recreé la base de datos en los cambios de la base de datos solo si está en modo de depuración ...). En este DatabaseInitializer, los índices se crean automáticamente en función de los atributos únicos:
Quizás esto ayude ...
fuente
Si anula el método ValidateEntity en su clase DbContext, también puede poner la lógica allí. La ventaja aquí es que tendrá acceso completo a todos sus DbSets. Aquí hay un ejemplo:
fuente
Si está utilizando EF5 y todavía tiene esta pregunta, la solución a continuación lo resolvió por mí.
Estoy usando el primer enfoque de código, por lo tanto, pongo:
en el script de migración hizo bien el trabajo. ¡También permite valores NULL!
fuente
Con el enfoque EF Code First, se puede implementar un soporte de restricción único basado en atributos utilizando la siguiente técnica.
Crear un atributo marcador
Marque las propiedades que desea que sean únicas en las entidades, por ejemplo
Cree un inicializador de base de datos o use uno existente para crear restricciones únicas
Configure el contexto de su base de datos para usar este inicializador en el código de inicio (por ejemplo, en
main()
oApplication_Start()
)La solución es similar a la de mheyman, con una simplificación de no admitir claves compuestas. Para ser utilizado con EF 5.0+.
fuente
Solución fluida de Api:
fuente
Hoy me enfrenté a ese problema y finalmente pude resolverlo. No sé si es un enfoque correcto, pero al menos puedo seguir adelante:
fuente
Use un validador de propiedad único.
ValidateEntity
no se llama dentro de la misma transacción de la base de datos. Por lo tanto, puede haber condiciones de carrera con otras entidades en la base de datos. Tienes que hackear EF para forzar una transacción alrededor deSaveChanges
(y, por lo tantoValidateEntity
).DBContext
no puede abrir la conexión directamente, peroObjectContext
puede.fuente
De acuerdo con http://blogs.msdn.com/b/adonet/archive/2014/02/11/ef-6-1-0-beta-1-available.aspx , EF 6.1 tendrá un IndexAttribute para ayudarnos .
fuente
Después de leer esta pregunta, tuve mi propia pregunta en el proceso de tratar de implementar un atributo para designar propiedades como claves únicas como las respuestas de Mihkel Müür , Tobias Schittkowski y mheyman sugieren: Asigna las propiedades del código del Marco de entidades a las columnas de la base de datos (CSpace a SSpace)
Finalmente llegué a esta respuesta, que puede asignar propiedades escalares y de navegación a las columnas de la base de datos y crear un índice único en una secuencia específica designada en el atributo. Este código asume que ha implementado un atributo único con una propiedad de secuencia, y lo aplicó a las propiedades de clase de entidad EF que deberían representar la clave única de la entidad (que no sea la clave primaria).
Nota: Este código se basa en EF versión 6.1 (o posterior) que expone que
EntityContainerMapping
no está disponible en versiones anteriores.fuente
Para aquellos que usan las primeras configuraciones de código, también pueden usar el objeto IndexAttribute como ColumnAnnotation y establecer su propiedad IsUnique en true.
Por ejemplo:
Esto creará un índice único llamado IX_name en la columna Nombre.
fuente
Perdón por la respuesta tardía, pero me pareció bueno compartirlo contigo
He publicado sobre esto en el proyecto de código
En general, depende de los atributos que pones en las clases para generar tus índices únicos
fuente