2011-04-18 96 views
56

唯一鍵我已經在我的項目與EF代碼第一

public class Category 
{ 
    public Guid ID { get; set; } 
    [Required(ErrorMessage = "Title cannot be empty")] 
    public string Title { get; set; } 
} 

,我試圖讓Title作爲唯一鍵下面的模型,我用Google搜索解決方案,但找不到任何。 有什麼建議我該怎麼做?

回答

100

不幸的是,你不能首先在代碼中將它定義爲唯一鍵,因爲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()); 

編輯

現在(EF 6.1起),您可以輕鬆擁有獨特的約束,

[Index("TitleIndex", IsUnique = true)] 
public string Title { get; set; } 
+0

我使用MVC 3和EF 4,代碼無法識別context.Database.ExecuteSqlCommand中的ExecuteSqlCommand(「CREATE UNIQUE INDEX IX_Category_Title ON Categories(Title)」);這是關於版本還是其他? – Saeid 2011-11-16 10:48:31

+1

@Saeid:這是用於DbContext API(EFv4.1)的。 EFv4中沒有數據庫初始值設定項。 ObjectContext API提供了自己的方法來直接執行SQL - 「ExecuteStoreCommand」。 – 2011-11-16 11:01:38

+1

也是一個很好的方法來添加默認約束(例如,GETDATE()等) – 2012-09-28 13:49:09

2

這裏是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 
+0

哦來吧 - 添加一個簡潔的VB版本有什麼問題,對於vb用戶有完全相同的問題?這不是這個問題 - 不僅爲原始海報提供資源?另外,如上所述,實施有所不同。 – GilShalit 2014-06-23 04:44:53

21

首先創建自定義屬性類:

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

而且爲最後添加Initializ呃在Application.Start裏面Global.asax.cs

System.Data.Entity.Database.SetInitializer<MyApp.Models.DomainModels.myEntities>(new MyApp.Models.DomainModels.myEntities.Initializer()); 

就是這樣。基於在vb代碼https://stackoverflow.com/a/7426773

+1

幾個更正。 1.在ExecuteSqlCommand期間,tableName應該被括起來。2.如果你使用的是非複數名稱,使用else {tableName = t.Name} – James 2012-10-25 21:29:12

0

我創建這個類(這是從另一個Stackoverflow答案 - Execute a large SQL script (with GO commands)增強),它允許我將SQL腳本放入一個目錄,並讓他們都執行每次需要(種子或遷移)。在部署到生產環境後,我不打算將其打開,但在開發期間,每次重新創建數據庫時都可以輕鬆應用腳本。

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的解決方案是什麼樣子: