实体框架代码优先唯一约束

提问

是否可以使用流畅的语法或属性定义属性的唯一约束?如果没有,有什么解决办法?

我有一个主键用户类,但我想确保电子邮件地址也是唯一的。不直接编辑数据库是否可行?

解决方案(基于 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)");
}
}
}
}
67729 次浏览

在创建数据库时,我会执行完整的黑客操作来执行 SQL。我创建自己的 DatabaseInitializer 并从提供的初始化器之一继承。

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

这是我能找到的唯一可以插入 SQL 语句的地方。

这是从 CTP4。我不知道它是如何工作在 CTP5。

据我所知,目前没有办法使用实体框架来实现这一点。然而,这不仅仅是一个有唯一约束的问题... ... 您可能还需要创建索引、检查约束,以及可能的触发器和其他构造。使用代码优先设置的 这里有一个你可以使用的简单模式,尽管必须承认它不是数据库不可知的:

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

另一种选择是,如果您的域模型是在数据库中插入/更新数据的唯一方法,那么您可以自己实现惟一性要求,而不使用数据库。这是一个更加可移植的解决方案,强制您在代码中清楚地了解业务规则,但是使数据库对无效数据开放,从而导致后门操作。

只是试图找出是否有一种方法可以做到这一点,到目前为止我发现的唯一方法就是自己强制执行它,我创建了一个属性来添加到每个类中,在这个类中,你需要提供唯一字段的名称:

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

然后在我的课上我会加上:

[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]
}

最后,我将在我的存储库中添加一个方法,在 Add 方法中,或者像下面这样保存更改时:

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

不太好,因为我们需要依赖于反思,但这是迄今为止的方法,为我工作!= D

如果在 DbContext 类中重写 ValidateEntity 方法,也可以将逻辑放在那里。这里的优点是您可以完全访问所有的 DbSet。这里有一个例子:

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

我通过反思解决了这个问题(对不起,伙计们,VB.Net...)

首先,定义一个属性 UniqueAttribute:

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


End Class

然后,增强你的模型像

<Table("Person")> _
Public Class Person


<Unique()> _
Public Property Username() As String


End Class

最后,创建一个自定义 DatabaseInitializer (在我的版本中,我只在调试模式下重新创建 DB on DB 更改...)。在这个 DatabaseInitializer 中,索引是根据 Unique-Attritribute 自动创建的:

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

也许这能帮上忙。

使用唯一的属性验证器。

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。因此,数据库中的其他实体可能存在竞态条件。您必须在某种程度上修改 EF 以强制执行 SaveChanges周围的事务(因此,也就是 ValidateEntity)。DBContext不能直接打开连接,但 ObjectContext可以。

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

和这个没什么关系,但是在某些情况下可能会有帮助。

如果您希望在两列上创建一个惟一的复合索引,作为表的约束,那么在4.3版本中,您可以使用新的迁移机制来实现它:

基本上,您需要在您的一个迁移脚本中插入这样一个调用:

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

差不多是这样:

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

我今天面对这个问题,最终我能够解决它。我不知道这样做是否正确,但至少我可以继续下去:

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

一个简单的方法在可视化基本使用 EF5代码首次迁移

公共班级样本

    Public Property SampleId As Integer


<Required>
<MinLength(1),MaxLength(200)>


Public Property Code() As String

最后一节课

属性 MaxLlength 对于字符串类型的唯一索引非常重要

运行 cmd: update-database-verose

在运行 cmd 之后,添加-迁移1

在生成的文件中

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

与 Tobias Schittkowski 的答案相似,但是 C # 具有在约束中包含多个字段的能力。

要使用它,只需在希望唯一的任何字段上放置一个[ Unique ]。对于字符串,您必须执行以下操作(注意 MaxLlength 属性) :

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

因为默认字符串字段是 nvarchar (max) ,并且在键中不允许使用该字段。

对于约束中的多个字段,您可以:

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

首先,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; }
}

然后,包含一个有用的扩展,从类型中获取数据库表名称:

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

然后,数据库初始化程序:

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

如果你正在使用 EF5并且仍然有这个问题,下面的解决方案为我解决了它。

我使用代码优先的方法,因此把:

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

在迁移脚本中做了很好的工作。它还允许 NULL 值!

根据 http://blogs.msdn.com/b/adonet/archive/2014/02/11/ef-6-1-0-beta-1-available.aspx,EF 6.1将有一个 IndexAttribute 来帮助我们。

使用 EF Code First 方法,可以使用以下技术实现基于属性的唯一约束支持。

创建标记属性

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

标记您希望在实体上是唯一的属性,例如。

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

创建数据库初始值设定项或使用现有的初始值设定项来创建唯一约束

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

将数据库上下文设置为在启动代码中使用此初始值设定项(例如,在 main()Application_Start()中)

Database.SetInitializer(new DbInitializer());

解决方案类似于梅耶曼的,简化了不支持复合键。将与 EF 5.0 + 一起使用。

在阅读了这个问题之后,我有了自己的问题,在试图实现一个属性的过程中,将属性指定为唯一的键,如 米克尔 · 缪尔的Tobias Schittkowski 的梅曼的的答案建议: 将实体框架代码属性映射到数据库列(CSpace 到 SSpace)

我最终得到了这个答案,它可以将标量和导航属性映射到数据库列,并按照属性上指定的特定序列创建唯一的索引。这段代码假设您已经实现了一个带序列属性的 UniqueAttribute,并将其应用于 EF 实体类属性,这些属性应该表示实体的唯一键(主键除外)。

注意: 此代码依赖于 EF 版本6.1(或更高版本) ,该版本暴露了以前版本中不可用的 EntityContainerMapping

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

从 EF 6.1开始,现在就有可能:

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

严格来说,这将为您提供一个唯一索引,而不是唯一约束。

在6.1中,你还可以使用@mihkelmuur 的流畅语法版本,如下所示:

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

流畅法虽然不是完美的 IMO 方法,但至少现在是可行的。

更多细节请见 Arthur Vickers 的博客 http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/

对于那些使用代码优先配置的用户,还可以将 IndexAttribute 对象作为 ColumnAnnote 使用,并将其 IsUnique 属性设置为 true。

例如:

var indexAttribute = new IndexAttribute("IX_name", 1) {IsUnique = true};


Property(i => i.Name).HasColumnAnnotation("Index",new IndexAnnotation(indexAttribute));

这将在 Name 列上创建一个名为 IX _ Name 的唯一索引。

很抱歉这么晚才回答,但是我觉得和你一起分享是件好事

我已经在 代码项目上发布了这个

一般来说,它取决于放置在类上以生成唯一索引的属性

流畅的 Api 解决方案:

modelBuilder.Entity<User>(entity =>
{
entity.HasIndex(e => e.UserId)
.HasName("IX_User")
.IsUnique();


entity.HasAlternateKey(u => u.Email);


entity.HasIndex(e => e.Email)
.HasName("IX_Email")
.IsUnique();
});