Уникальный ключ с кодом EF первый
у меня есть следующая модель в моем проекте
public class Category
{
public Guid ID { get; set; }
[Required(ErrorMessage = "Title cannot be empty")]
public string Title { get; set; }
}
и я пытаюсь сделать Title
как уникальный ключ, я googled для решения, но не мог найти любой.
Может ли кто-нибудь предложить мне, как это сделать, пожалуйста?
5 ответов:
к сожалению, вы не можете сначала определить его как уникальный ключ в коде, потому что EF вообще не поддерживает уникальные ключи (мы надеемся, что он будет выпущен в следующем крупном выпуске). Что вы можете сделать, это создать пользовательский инициализатор базы данных и добавить уникальный индекс вручную, вызвав команду 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());
Edit
Теперь (EF 6.1 и далее )вы можете легко иметь уникальный сдерживает ,
[Index("TitleIndex", IsUnique = true)] public string Title { get; set; }
сначала создайте пользовательский класс атрибутов:
[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:
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 + ")"); } } } } } } }
и для последнего добавьте инициализатор в Application_Start внутри Global.асакс.cs
System.Data.Entity.Database.SetInitializer<MyApp.Models.DomainModels.myEntities>(new MyApp.Models.DomainModels.myEntities.Initializer());
вот и все. на основе кода vb в https://stackoverflow.com/a/7426773
вот VB.Net версия-обратите внимание на реализацию дженериков, которая немного отличается, на уровне класса.
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
Я создаю этот класс (который WS улучшен из другого ответа Stackoverflow -выполнить большой SQL-скрипт (с командами GO)), что позволяет мне помещать сценарии SQL в каталог и выполнять их все каждый раз, когда они требуются (Seed или миграция). Я не собираюсь оставлять это открытым после развертывания в производство, но во время разработки это позволяет легко применять сценарии каждый раз, когда БД воссоздается.
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 выглядит так: