net framework first data constraint code entity-framework code-first unique-constraint

entity-framework - data - entity framework core code first unique



Restricción única en el código del marco de la entidad primero (19)

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


Comenzando con EF 6.1 ahora es posible:

[Index(IsUnique = true)] public string EmailAddress { get; set; }

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 .


Como no hay una anotación incorporada, descubrí una solución alternativa. Por favor, consulte este enlace para más información https://.com/a/16496291/1873113


Con el primer enfoque de EF Code First, se puede implementar un soporte de restricción único basado en atributos usando la siguiente técnica.

Crear un atributo marcador

[AttributeUsage(AttributeTargets.Property)] public class UniqueAttribute : System.Attribute { }

Marque propiedades que le gustaría ser único en entidades, por ejemplo

[Unique] public string EmailAddress { get; set; }

Cree un inicializador de base de datos o use uno existente para crear las restricciones únicas

public class DbInitializer : IDatabaseInitializer<DbContext> { public void InitializeDatabase(DbContext db) { if (db.Database.Exists() && !db.Database.CompatibleWithModel(false)) { db.Database.Delete(); } if (!db.Database.Exists()) { db.Database.Create(); CreateUniqueIndexes(db); } } private static void CreateUniqueIndexes(DbContext db) { var props = from p in typeof(AppDbContext).GetProperties() where p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) select p; foreach (var prop in props) { var type = prop.PropertyType.GetGenericArguments()[0]; var fields = from p in type.GetProperties() where p.GetCustomAttributes(typeof(UniqueAttribute), true).Any() select p.Name; foreach (var field in fields) { const string sql = "ALTER TABLE dbo.[{0}] ADD CONSTRAINT" + " [UK_dbo.{0}_{1}] UNIQUE ([{1}])"; var command = String.Format(sql, type.Name, field); db.Database.ExecuteSqlCommand(command); } } } }

Establezca el contexto de su base de datos para usar este inicializador en el código de inicio (por ejemplo, en main() o Application_Start() )

Database.SetInitializer(new DbInitializer());

La solución es similar a la de mheyman, con una simplificación de no soportar claves compuestas. Para ser utilizado con EF 5.0+.



Después de leer esta pregunta, tuve mi propia pregunta en el proceso de intentar implementar un atributo para designar propiedades, ya que claves únicas como las respuestas de Mihkel Müür , Tobias Schittkowski y mheyman''s sugieren: Propiedades de código de Map Entity Framework a columnas de base de datos (CSpace a SSpace)

Finalmente llegué a esta respuesta que puede asignar tanto propiedades escalares como de navegación a columnas de bases de datos y crear un índice único en una secuencia específica designada en el atributo. Este código supone que ha implementado un UniqueAttribute con una propiedad Sequence, y lo ha aplicado a las propiedades de clase de entidad EF que deberían representar la clave única de la entidad (que no sea la clave principal).

Nota: Este código se basa en EF versión 6.1 (o posterior) que expone a EntityContainerMapping no disponible en versiones anteriores.

Public Sub InitializeDatabase(context As MyDB) Implements IDatabaseInitializer(Of MyDB).InitializeDatabase If context.Database.CreateIfNotExists Then Dim ws = DirectCast(context, System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext.MetadataWorkspace Dim oSpace = ws.GetItemCollection(Core.Metadata.Edm.DataSpace.OSpace) Dim entityTypes = oSpace.GetItems(Of EntityType)() Dim entityContainer = ws.GetItems(Of EntityContainer)(DataSpace.CSpace).Single() Dim entityMapping = ws.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).Single.EntitySetMappings Dim associations = ws.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).Single.AssociationSetMappings For Each setType In entityTypes Dim cSpaceEntitySet = entityContainer.EntitySets.SingleOrDefault( _ Function(t) t.ElementType.Name = setType.Name) If cSpaceEntitySet Is Nothing Then Continue For '' Derived entities will be skipped Dim sSpaceEntitySet = entityMapping.Single(Function(t) t.EntitySet Is cSpaceEntitySet) Dim tableInfo As MappingFragment If sSpaceEntitySet.EntityTypeMappings.Count = 1 Then tableInfo = sSpaceEntitySet.EntityTypeMappings.Single.Fragments.Single Else '' Select only the mapping (esp. PropertyMappings) for the base class tableInfo = sSpaceEntitySet.EntityTypeMappings.Where(Function(m) m.IsOfEntityTypes.Count _ = 1 AndAlso m.IsOfEntityTypes.Single.Name Is setType.Name).Single().Fragments.Single End If Dim tableName = If(tableInfo.StoreEntitySet.Table, tableInfo.StoreEntitySet.Name) Dim schema = tableInfo.StoreEntitySet.Schema Dim clrType = Type.GetType(setType.FullName) Dim uniqueCols As IList(Of String) = Nothing For Each propMap In tableInfo.PropertyMappings.OfType(Of ScalarPropertyMapping)() Dim clrProp = clrType.GetProperty(propMap.Property.Name) If Attribute.GetCustomAttribute(clrProp, GetType(UniqueAttribute)) IsNot Nothing Then If uniqueCols Is Nothing Then uniqueCols = New List(Of String) uniqueCols.Add(propMap.Column.Name) End If Next For Each navProp In setType.NavigationProperties Dim clrProp = clrType.GetProperty(navProp.Name) If Attribute.GetCustomAttribute(clrProp, GetType(UniqueAttribute)) IsNot Nothing Then Dim assocMap = associations.SingleOrDefault(Function(a) _ a.AssociationSet.ElementType.FullName = navProp.RelationshipType.FullName) Dim sProp = assocMap.Conditions.Single If uniqueCols Is Nothing Then uniqueCols = New List(Of String) uniqueCols.Add(sProp.Column.Name) End If Next If uniqueCols IsNot Nothing Then Dim propList = uniqueCols.ToArray() context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_" & tableName & "_" & String.Join("_", propList) _ & " ON " & schema & "." & tableName & "(" & String.Join(",", propList) & ")") End If Next End If End Sub


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.

public class MyDatabaseInitializer : RecreateDatabaseIfModelChanges<MyDbContext> { protected override void Seed(MyDbContext context) { base.Seed(context); context.Database.Connection.StateChange += new StateChangeEventHandler(Connection_StateChange); } void Connection_StateChange(object sender, StateChangeEventArgs e) { DbConnection cnn = sender as DbConnection; if (e.CurrentState == ConnectionState.Open) { // execute SQL to create indexes and such } cnn.StateChange -= Connection_StateChange; } }

Ese es el único lugar que pude encontrar para encajar en mis declaraciones de SQL.

Esto es de CTP4. No sé cómo funciona en CTP5.


Hoy enfrenté ese problema y finalmente pude resolverlo. No sé si es un enfoque correcto, pero al menos puedo seguir:

public class Person : IValidatableObject { public virtual int ID { get; set; } public virtual string Name { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var field = new[] { "Name" }; // Must be the same as the property PFContext db = new PFContext(); Person person = validationContext.ObjectInstance as Person; var existingPerson = db.Persons.FirstOrDefault(a => a.Name == person.Name); if (existingPerson != null) { yield return new ValidationResult("That name is already in the db", field); } } }


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 migración para lograrlo:

Básicamente, debe insertar una llamada como esta en uno de sus scripts de migración:

CreateIndex("TableName", new string[2] { "Column1", "Column2" }, true, "IX_UniqueColumn1AndColumn2");

Algo como eso:

namespace Sample.Migrations { using System; using System.Data.Entity.Migrations; public partial class TableName_SetUniqueCompositeIndex : DbMigration { public override void Up() { CreateIndex("TableName", new[] { "Column1", "Column2" }, true, "IX_UniqueColumn1AndColumn2"); } public override void Down() { DropIndex("TableName", new[] { "Column1", "Column2" }); } } }


Para aquellos que usan primeras configuraciones de código, también puede usar el objeto IndexAttribute como una Anotación de columna y establecer su propiedad IsUnique en verdadero.

Por ejemplo:

var indexAttribute = new IndexAttribute("IX_name", 1) {IsUnique = true}; Property(i => i.Name).HasColumnAnnotation("Index",new IndexAnnotation(indexAttribute));

Esto creará un índice único llamado IX_name en la columna Nombre.


Perdón por la respuesta tardía, pero me pareció bueno compartirla contigo

He publicado sobre esto en el proyecto de código

En general, depende de los atributos que coloque en las clases para generar sus índices únicos


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 desencadenantes y otras construcciones también. Aquí hay un patrón simple que puede usar con su configuración de primer código, aunque no es agnóstico de la base de datos:

public class MyRepository : DbContext { public DbSet<Whatever> Whatevers { get; set; } public class Initializer : IDatabaseInitializer<MyRepository> { public void InitializeDatabase(MyRepository context) { if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase()) { context.Database.DeleteIfExists(); context.Database.Create(); context.ObjectContext.ExecuteStoreCommand("CREATE UNIQUE CONSTRAINT..."); context.ObjectContext.ExecuteStoreCommand("CREATE INDEX..."); context.ObjectContext.ExecuteStoreCommand("ETC..."); } } } }

Otra opción es si su modelo de dominio es el único método para insertar / actualizar datos en su base de datos, puede 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 te obliga a ser claro sobre las reglas de tu negocio en tu código, pero deja tu base de datos abierta a que los datos no válidos se vuelvan a incluir.


Resolví el problema por reflexión (lo siento, amigos, VB.Net ...)

Primero, defina un atributo UniqueAttribute:

<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=True)> _ Public Class UniqueAttribute Inherits Attribute End Class

Entonces, mejore su modelo como

<Table("Person")> _ Public Class Person <Unique()> _ Public Property Username() As String End Class

Finalmente, cree un DatabaseInitializer personalizado (en mi versión, recrearé el DB en cambios DB solo si está en modo de depuración ...). En este DatabaseInitializer, los índices se crean automáticamente en función de los atributos únicos:

Imports System.Data.Entity Imports System.Reflection Imports System.Linq Imports System.ComponentModel.DataAnnotations Public Class DatabaseInitializer Implements IDatabaseInitializer(Of DBContext) Public Sub InitializeDatabase(context As DBContext) Implements IDatabaseInitializer(Of DBContext).InitializeDatabase Dim t As Type Dim tableName As String Dim fieldName As String If Debugger.IsAttached AndAlso context.Database.Exists AndAlso Not context.Database.CompatibleWithModel(False) Then context.Database.Delete() End If If Not context.Database.Exists Then context.Database.Create() For Each pi As PropertyInfo In GetType(DBContext).GetProperties If pi.PropertyType.IsGenericType AndAlso _ pi.PropertyType.Name.Contains("DbSet") Then t = pi.PropertyType.GetGenericArguments(0) tableName = t.GetCustomAttributes(True).OfType(Of TableAttribute).FirstOrDefault.Name For Each piEntity In t.GetProperties If piEntity.GetCustomAttributes(True).OfType(Of Model.UniqueAttribute).Any Then fieldName = piEntity.Name context.Database.ExecuteSqlCommand("ALTER TABLE " & tableName & " ADD CONSTRAINT con_Unique_" & tableName & "_" & fieldName & " UNIQUE (" & fieldName & ")") End If Next End If Next End If End Sub End Class

Tal vez esto ayude ...


Si anula el método ValidateEntity en su clase DbContext, puede poner la lógica allí también. La ventaja aquí es que tendrá acceso completo a todos sus DbSets. Aquí hay un ejemplo:

using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.ModelConfiguration.Conventions; using System.Data.Entity.Validation; using System.Linq; namespace MvcEfClient.Models { public class Location { [Key] public int LocationId { get; set; } [Required] [StringLength(50)] public string Name { get; set; } } public class CommitteeMeetingContext : DbContext { protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) { List<DbValidationError> validationErrors = new List<DbValidationError>(); // Check for duplicate location names if (entityEntry.Entity is Location) { Location location = entityEntry.Entity as Location; // Select the existing location var existingLocation = (from l in Locations where l.Name == location.Name && l.LocationId != location.LocationId select l).FirstOrDefault(); // If there is an existing location, throw an error if (existingLocation != null) { validationErrors.Add(new DbValidationError("Name", "There is already a location with the name ''" + location.Name + "''")); return new DbEntityValidationResult(entityEntry, validationErrors); } } return base.ValidateEntity(entityEntry, items); } public DbSet<Location> Locations { get; set; } } }


Si está utilizando EF5 y todavía tiene esta pregunta, la solución que se encuentra a continuación lo resolvió para mí.

Estoy usando el primer acercamiento del código, por lo tanto poniendo:

this.Sql("CREATE UNIQUE NONCLUSTERED INDEX idx_unique_username ON dbo.Users(Username) WHERE Username IS NOT NULL;");

en el script de migración hizo bien el trabajo. ¡También permite valores NULL!


Similar a la respuesta de Tobias Schittkowski, pero C # y tiene la capacidad de tener múltiples campos en las contiendas.

Para usar esto, solo coloque un [Único] en cualquier campo que desee que sea único. Para cadenas, tendrá que hacer algo como (tenga en cuenta el atributo MaxLength):

[Unique] [MaxLength(450)] // nvarchar(450) is max allowed to be in a key public string Name { get; set; }

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 que puede hacer:

[Unique(Name="UniqueValuePairConstraint", Position=1)] public int Value1 { get; set; } [Unique(Name="UniqueValuePairConstraint", Position=2)] public int Value2 { get; set; }

Primero, el UniqueAttribute:

/// <summary> /// The unique attribute. Use to mark a field as unique. The /// <see cref="DatabaseInitializer"/> looks for this attribute to /// create unique constraints in tables. /// </summary> internal class UniqueAttribute : Attribute { /// <summary> /// Gets or sets the name of the unique constraint. A name will be /// created for unnamed unique constraints. You must name your /// constraint if you want multiple fields in the constraint. If your /// constraint has only one field, then this property can be ignored. /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the position of the field in the constraint, lower /// numbers come first. The order is undefined for two fields with /// the same position. The default position is 0. /// </summary> public int Position { get; set; } }

Luego, incluya una extensión útil para obtener el nombre de la tabla de la base de datos de un tipo:

public static class Extensions { /// <summary> /// Get a table name for a class using a DbContext. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="type"> /// The class to look up the table name for. /// </param> /// <returns> /// The table name; null on failure; /// </returns> /// <remarks> /// <para> /// Like: /// <code> /// DbContext context = ...; /// string table = context.GetTableName&lt;Foo&gt;(); /// </code> /// </para> /// <para> /// This code uses ObjectQuery.ToTraceString to generate an SQL /// select statement for an entity, and then extract the table /// name from that statement. /// </para> /// </remarks> public static string GetTableName(this DbContext context, Type type) { return ((IObjectContextAdapter)context) .ObjectContext.GetTableName(type); } /// <summary> /// Get a table name for a class using an ObjectContext. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="type"> /// The class to look up the table name for. /// </param> /// <returns> /// The table name; null on failure; /// </returns> /// <remarks> /// <para> /// Like: /// <code> /// ObjectContext context = ...; /// string table = context.GetTableName&lt;Foo&gt;(); /// </code> /// </para> /// <para> /// This code uses ObjectQuery.ToTraceString to generate an SQL /// select statement for an entity, and then extract the table /// name from that statement. /// </para> /// </remarks> public static string GetTableName(this ObjectContext context, Type type) { var genericTypes = new[] { type }; var takesNoParameters = new Type[0]; var noParams = new object[0]; object objectSet = context.GetType() .GetMethod("CreateObjectSet", takesNoParameters) .MakeGenericMethod(genericTypes) .Invoke(context, noParams); var sql = (string)objectSet.GetType() .GetMethod("ToTraceString", takesNoParameters) .Invoke(objectSet, noParams); Match match = Regex.Match(sql, @"FROM/s+(.*)/s+AS", RegexOptions.IgnoreCase); return match.Success ? match.Groups[1].Value : null; } }

Entonces, el inicializador de la base de datos:

/// <summary> /// The database initializer. /// </summary> public class DatabaseInitializer : IDatabaseInitializer<PedContext> { /// <summary> /// Initialize the database. /// </summary> /// <param name="context"> /// The context. /// </param> public void InitializeDatabase(FooContext context) { // if the database has changed, recreate it. if (context.Database.Exists() && !context.Database.CompatibleWithModel(false)) { context.Database.Delete(); } if (!context.Database.Exists()) { context.Database.Create(); // Look for database tables in the context. Tables are of // type DbSet<>. foreach (PropertyInfo contextPropertyInfo in context.GetType().GetProperties()) { var contextPropertyType = contextPropertyInfo.PropertyType; if (contextPropertyType.IsGenericType && contextPropertyType.Name.Equals("DbSet`1")) { Type tableType = contextPropertyType.GetGenericArguments()[0]; var tableName = context.GetTableName(tableType); foreach (var uc in UniqueConstraints(tableType, tableName)) { context.Database.ExecuteSqlCommand(uc); } } } // this is a good place to seed the database context.SaveChanges(); } } /// <summary> /// Get a list of TSQL commands to create unique constraints on the given /// table. Looks through the table for fields with the UniqueAttribute /// and uses those and the table name to build the TSQL strings. /// </summary> /// <param name="tableClass"> /// The class that expresses the database table. /// </param> /// <param name="tableName"> /// The table name in the database. /// </param> /// <returns> /// The list of TSQL statements for altering the table to include unique /// constraints. /// </returns> private static IEnumerable<string> UniqueConstraints( Type tableClass, string tableName) { // the key is the name of the constraint and the value is a list // of (position,field) pairs kept in order of position - the entry // with the lowest position is first. var uniqueConstraints = new Dictionary<string, List<Tuple<int, string>>>(); foreach (PropertyInfo entityPropertyInfo in tableClass.GetProperties()) { var unique = entityPropertyInfo.GetCustomAttributes(true) .OfType<UniqueAttribute>().FirstOrDefault(); if (unique != null) { string fieldName = entityPropertyInfo.Name; // use the name field in the UniqueAttribute or create a // name if none is given string constraintName = unique.Name ?? string.Format( "constraint_{0}_unique_{1}", tableName .Replace("[", string.Empty) .Replace("]", string.Empty) .Replace(".", "_"), fieldName); List<Tuple<int, string>> constraintEntry; if (!uniqueConstraints.TryGetValue( constraintName, out constraintEntry)) { uniqueConstraints.Add( constraintName, new List<Tuple<int, string>> { new Tuple<int, string>( unique.Position, fieldName) }); } else { // keep the list of fields in order of position for (int i = 0; ; ++i) { if (i == constraintEntry.Count) { constraintEntry.Add( new Tuple<int, string>( unique.Position, fieldName)); break; } if (unique.Position < constraintEntry[i].Item1) { constraintEntry.Insert( i, new Tuple<int, string>( unique.Position, fieldName)); break; } } } } } return uniqueConstraints.Select( uc => string.Format( "ALTER TABLE {0} ADD CONSTRAINT {1} UNIQUE ({2})", tableName, uc.Key, string.Join(",", uc.Value.Select(v => v.Item2)))); } }


Solo tratando de descubrir si había una manera de hacerlo, solo la forma que encontré hasta ahora fue hacerla cumplir por mi cuenta, creé un atributo para ser agregado a cada clase donde proporcione el nombre de los campos que necesita para ser único:

[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=false,Inherited=true)] public class UniqueAttribute:System.Attribute { private string[] _atts; public string[] KeyFields { get { return _atts; } } public UniqueAttribute(string keyFields) { this._atts = keyFields.Split(new char[]{'',''}, StringSplitOptions.RemoveEmptyEntries); } }

Luego en mi clase lo agregaré:

[CustomAttributes.Unique("Name")] public class Item: BasePOCO { public string Name{get;set;} [StringLength(250)] public string Description { get; set; } [Required] public String Category { get; set; } [Required] public string UOM { get; set; } [Required] }

Finalmente, agregaré un método en mi repositorio, en el método Add o cuando guarde cambios como este:

private void ValidateDuplicatedKeys(T entity) { var atts = typeof(T).GetCustomAttributes(typeof(UniqueAttribute), true); if (atts == null || atts.Count() < 1) { return; } foreach (var att in atts) { UniqueAttribute uniqueAtt = (UniqueAttribute)att; var newkeyValues = from pi in entity.GetType().GetProperties() join k in uniqueAtt.KeyFields on pi.Name equals k select new { KeyField = k, Value = pi.GetValue(entity, null).ToString() }; foreach (var item in _objectSet) { var keyValues = from pi in item.GetType().GetProperties() join k in uniqueAtt.KeyFields on pi.Name equals k select new { KeyField = k, Value = pi.GetValue(item, null).ToString() }; var exists = keyValues.SequenceEqual(newkeyValues); if (exists) { throw new System.Exception("Duplicated Entry found"); } } } }

No es muy agradable ya que necesitamos confiar en la reflexión, pero hasta ahora este es el enfoque que funciona para mí. = D


También en 6.1 puedes usar la versión de sintaxis fluida de la respuesta de @ mihkelmuur como sigue:

Property(s => s.EmailAddress).HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation( new IndexAttribute("IX_UniqueEmail") { IsUnique = true }));

El método fluido no es IMO perfecto, pero al menos es posible ahora.

Más deets en el blog de Arthur Vickers http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/


Una forma sencilla en Visual Basic usando EF5 Code First Migrations

Muestra de clase pública

Public Property SampleId As Integer <Required> <MinLength(1),MaxLength(200)> Public Property Code() As String

Clase final

El atributo MaxLength es muy importante para el índice único del tipo de cadena

Ejecute cmd: update-database -verbose

después de ejecutar cmd: add-migration 1

en el archivo generado

Public Partial Class _1 Inherits DbMigration Public Overrides Sub Up() CreateIndex("dbo.Sample", "Code", unique:=True, name:="IX_Sample_Code") End Sub Public Overrides Sub Down() ''DropIndex if you need it End Sub End Class


Use un validador de propiedad único.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) { var validation_state = base.ValidateEntity(entityEntry, items); if (entityEntry.Entity is User) { var entity = (User)entityEntry.Entity; var set = Users; //check name unique if (!(set.Any(any_entity => any_entity.Name == entity.Name))) {} else { validation_state.ValidationErrors.Add(new DbValidationError("Name", "The Name field must be unique.")); } } return validation_state; }

ValidateEntity no se llama dentro de la misma transacción de base de datos. Por lo tanto, puede haber condiciones de carrera con otras entidades en la base de datos. Tienes que hackear EF algo para forzar una transacción alrededor de los SaveChanges (y por lo tanto, ValidateEntity ). DBContext no puede abrir la conexión directamente, pero ObjectContext puede.

using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required)) { ((IObjectContextAdapter)data_context).ObjectContext.Connection.Open(); data_context.SaveChanges(); transaction.Complete(); }