2011-11-24 107 views
0

我認爲這應該是直截了當的,但對於NHibernate而言,簡單的事情總是最複雜的。流利的NHibernate,HasManytoMany和主鍵問題

我有過HasManyToMany引用兩個彼此波蘇斯:

public class Foo1 
{ 
//other properties 
public virtual IList<Foo2> Foo2s {get;set;} 
} 
public class Foo2 
{ 
//other properties 
public virtual IList<Foo1> Foo1s {get;set;} 
} 

而且映射:

class Foo1Map : ClassMap<Foo1> 
{ 
//other mappings 
HasManyToMany(c => c.Foo2s); 
} 
class Foo2Map : ClassMap<Foo2> 
{ 
//other mappings 
HasManyToMany(c => c.Foo1s); 
} 

連接表創建正確,它有2個領域,其對外國鍵各自的表格,一切正常,巨大的問題是,這兩個領域應該是主鍵(或至少是唯一的),而他們不是。我試着用HasManyToMany鏈接各種流暢的方法,但沒有任何結果。

如何讓生成的manytomany表具有2個外鍵字段的主鍵,而無需創建自定義POCO並將其與複合鍵映射到一起?

謝謝。

回答

3

唯一約束,也指數

class Foo1Map : ClassMap<Foo1> 
{ 
    //other mappings 
    HasManyToMany(c => c.Foo2s) 
     .ParentKeyColumns.Add("foo1_id", p => p.UniqueKey("unique_constraint1").Index("someIndex")) 
     .ChildKeyColumns.Add("foo2_id", p => p.UniqueKey("unique_constraint1")); 
} 

更新:或作爲約定的hasMany(這樣的代碼我在一個項目中使用),而不是很好,但工作

public class IndexOneToManyColumnsConvention : IHasManyConvention 
{ 
    public void Apply(IOneToManyCollectionInstance instance) 
    { 
     var mappingfield = instance.Key.Columns.First().GetType() 
      .GetField("mapping", BindingFlags.Instance | BindingFlags.NonPublic); 

     var columnMapping = (ColumnMapping)mappingfield.GetValue(instance.Key.Columns.First()); 

     if (!columnMapping.HasValue(c => c.Index)) 
     { 
      var typename = instance.Member.DeclaringType.Name; 

      columnMapping.Index = string.Format("index_{0}_{1}", 
       typename.ToLower(), instance.Member.Name.ToLower()); 
     } 
    } 
} 

我認爲在FNH的後備箱中有代碼來擺脫這種破解

+0

任何想法,如果這在三年後以一種更好的方式處理以來?否則,感謝您的信息。 – Ted

+0

@我不知道。但是我懷疑它很快就會出現,因爲NHibernate引入了MappingByCode,因此項目處於維護模式。 – Firo

+0

感謝info @Firo – Ted

1

我一直在遇到流暢的限制,並放棄使用它設置更高級的架構調整。我在查找表上設置了索引,所以只是將SQL添加到了一個後期創建腳本中。

我們有一個數據庫項目,它使用NHibernate創建數據庫,然後運行一個創建索引和我們使用的存儲過程的創建腳本創建腳本。

我知道這並不直接回答你的問題,但昨天我一直在努力解決這個問題,所以我認爲我會分享我的解決方案和一些代碼給你一個開始,如果這是你決定頭的路線下。

有興趣看看其他人在這裏做了什麼!

private void ExecuteScripts(string dir, bool useTransaction = true, bool useMasterConnection = false) 
{ 
    foreach (var file in SqlFiles.Where(x => x.Contains(dir))) 
    { 
     var resource = file.Substring((Assembly.GetExecutingAssembly().GetName().Name + ".scripts.").Length);    

     if (useTransaction) 
      ExecuteSqlWithTransaction(resource, useMasterConnection); 
     else 
      ExecuteSqlWithoutTransaction(resource, useMasterConnection); 
    } 
} 



private void ExecuteSqlWithoutTransaction(string sqlFile, bool useMasterConnection = false) 
     { 
      var sql = string.Empty; 

      var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream((Assembly.GetExecutingAssembly().GetName().Name + ".scripts.") + sqlFile.Replace('\\', '.')); 

      using (var reader = new StreamReader(stream)) 
      { 
       sql = reader.ReadToEnd(); 
      } 

      // replace the [[[dbName]]] and [[[SQLDATAFILEPATH]]] with ACTUAL DB settings we want to work with 
      sql = sql.Replace("[[[DBNAME]]]", DbName); 
      sql = sql.Replace("[[[SQLDATAFILEPATH]]]", SqlDataFilePath); 

      var regex = new Regex("^GO", RegexOptions.IgnoreCase | RegexOptions.Multiline); 
      var lines = regex.Split(sql); 

      var currentConnection = (useMasterConnection) ? MasterSqlConnection : SqlConnection; 

      using (var cmd = currentConnection.CreateCommand()) 
      { 
       cmd.Connection = currentConnection; 

       foreach (var line in lines) 
       { 
        if (line.Length > 0) 
        { 
         cmd.CommandText = line; 
         cmd.CommandType = CommandType.Text; 

         try 
         { 
          cmd.ExecuteNonQuery(); 
         } 
         catch (SqlException ex) 
         { 
          //ShowInfo("Error running script " + sqlFile + " Error: " + ex.Message); 
          throw new Exception(ex.Message); 
         } 
        } 
       } 
      } 

     } 



ALTER TABLE [dbo].[ArticleProjectAssignments] ADD CONSTRAINT [pk_ArticleProjectAssignments] PRIMARY KEY CLUSTERED 
(
    [ArticleId] ASC, 
    [ProjectId] ASC 
) 
GO 
+0

它肯定是一個值得注意的解決方法。我沒有時間/資源去嘗試,通常我不使用NHibernate,被迫使用這個項目來使用它,否則我都是EF Code First 4.1,它更先進,更易於使用等,謝謝你的回覆。 –

+0

@MatteoMosca出於興趣,確實創建EF自己的主鍵,或者你如何告訴它做到這一點? – Firo

+0

EF會自動將主鍵放在自動生成的多對多表上,就像我的示例一樣。我認爲這是正確的,沒有理由主鍵不應該在多對多表的唯一兩個字段上。對於一個「多對多」但具有其他字段的表格,這種情況不同,在這種情況下,您有POCO映射到它,等等。 –