首先使用EF代码的唯一键
我在我的项目中有一个以下的模型
public class Category { public Guid ID { get; set; } [Required(ErrorMessage = "Title cannot be empty")] public string Title { get; set; } }
我试图把Title
作为唯一的关键,我search解决scheme,但找不到任何。 有什么build议我怎么做,请吗?
不幸的是,你不能首先在代码中定义它作为唯一键,因为EF根本不支持唯一键(有望在下一个主要版本中计划)。 你可以做的是创build自定义数据库初始化器,并通过调用SQL命令手动添加唯一索引:
public class MyInitializer : CreateDatabaseIfNotExists<MyContext> { protected override void Seed(MyContext context) { context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Category_Title ON Categories (Title)"); } }
你必须在应用程序的引导程序中设置这个初始值设定项。
Database.SetInitializer<MyContext>(new MyInitializer());
编辑
现在(EF 6.1以上),你可以很容易地有独特的约束,
[Index("TitleIndex", IsUnique = true)] public string Title { get; set; }
首先创build自定义属性类:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class UniqueAttribute : ValidationAttribute { public override Boolean IsValid(Object value) { // constraint implemented on database return true; } }
然后添加到你的类:
public class Email { [Key] public int EmailID { get; set; } public int PersonId { get; set; } [Unique] [Required] [MaxLength(100)] public string EmailAddress { get; set; } public virtual bool IsDefault { get; set; } public virtual Boolean IsApprovedForLogin { get; set; } public virtual String ConfirmationToken { get; set; } [ForeignKey("PersonId")] public virtual Person Person { get; set; } }
然后在你的DbContext上添加一个Initializer:
public class Initializer : IDatabaseInitializer<myEntities> { public void InitializeDatabase(myEntities context) { if (System.Diagnostics.Debugger.IsAttached && context.Database.Exists() && !context.Database.CompatibleWithModel(false)) { context.Database.Delete(); } if (!context.Database.Exists()) { context.Database.Create(); var contextObject = context as System.Object; var contextType = contextObject.GetType(); var properties = contextType.GetProperties(); System.Type t = null; string tableName = null; string fieldName = null; foreach (var pi in properties) { if (pi.PropertyType.IsGenericType && pi.PropertyType.Name.Contains("DbSet")) { t = pi.PropertyType.GetGenericArguments()[0]; var mytableName = t.GetCustomAttributes(typeof(TableAttribute), true); if (mytableName.Length > 0) { TableAttribute mytable = mytableName[0] as TableAttribute; tableName = mytable.Name; } else { tableName = pi.Name; } foreach (var piEntity in t.GetProperties()) { if (piEntity.GetCustomAttributes(typeof(UniqueAttribute), true).Length > 0) { fieldName = piEntity.Name; context.Database.ExecuteSqlCommand("ALTER TABLE " + tableName + " ADD CONSTRAINT con_Unique_" + tableName + "_" + fieldName + " UNIQUE (" + fieldName + ")"); } } } } } } }
最后在Global.asax.cs中的Application_Start中添加Initializer
System.Data.Entity.Database.SetInitializer<MyApp.Models.DomainModels.myEntities>(new MyApp.Models.DomainModels.myEntities.Initializer());
而已。 基于vb代码在https://stackoverflow.com/a/7426773
这里是VB.Net版本 – 请注意在类级别上generics的实现有点不同。
Public Class MyInitializer(Of T As DbContext) Inherits CreateDatabaseIfNotExists(Of T) Protected Overrides Sub Seed(context As T) context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Category_Title ON Categories (Title)") End Sub End Class
我创build了这个类(这是从另一个Stackoverflow答案增强 – 执行一个大的SQL脚本(与GO命令) ),它允许我把SQL脚本放入一个目录,并让他们都执行每次需要(种子或迁移)。 在部署到生产环境后,我不打算将其打开,但是在开发过程中,每次重新创build数据库时都可以轻松应用脚本。
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; //dll Microsoft.SqlServer.Smo //dll Microsoft.SqlServer.Management.Sdk.Sfc //dll Microsoft.SqlServer.ConnectionInfo using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Smo; using Monitor.Common; namespace MonitorDB.DataLayer.Migrations { public class ExecuteSQLScripts :Monitor.Common.ExceptionHandling { public ExecuteSQLScripts() { } public bool ExecuteScriptsInDirectory(DBContext.SolArcMsgMonitorContext context, string scriptDirectory) { bool Result = false; try { SqlConnection connection = new SqlConnection(context.Database.Connection.ConnectionString); Server server = new Server(new ServerConnection(connection)); DirectoryInfo di = new DirectoryInfo(scriptDirectory); FileInfo[] rgFiles = di.GetFiles("*.sql"); foreach (FileInfo fi in rgFiles) { FileInfo fileInfo = new FileInfo(fi.FullName); string script = fileInfo.OpenText().ReadToEnd(); server.ConnectionContext.ExecuteNonQuery(script); } Result = true; } catch (Exception ex) { CatchException("ExecuteScriptsInDirectory", ex); } return Result; }
}}
这是什么VS解决scheme看起来像:
我发现这个解决scheme虽然没有在SQL级别创build唯一键,但它使用DataAnnotationsvalidation,检查出来: