2011-03-24 160 views
8

我正在努力解決一個非常乏味的問題。 我有一個名爲國家級和國家稱爲類多重約束違反SQL Server 2008 - CodeFirst

public class Nation 
{ 
    public int ID {get; set;} 
    public int name {get;set;} 
    public List<NationAlly> NationAllies {get;set;} 
} 

public class NationAlly 
{ 
    public int ID {get; set;} 
    public int level {get;set;} 
    public Nation toNation {get;set;} 
} 

我使用EF 4 CodeFirst用的DbContext叫NationsDB來管理我的數據庫SQL Server 2008的 。如果我創建類型的新對象我嘗試撥打countriesDB.SaveChanges,我得到以下例外:

「違反多重性約束'關係'CodeFirstNamespace.NationAlly_toNation'的角色'NationAlly_toNation_Target'具有多重性1或0..1。」

我試圖用NationAllies字段爲空來保存Nation,並且這個異常不會拋出,數據庫中的Nation表會得到所有正確的值。

在我的數據庫表國家有2個字段:ID(主鍵),名稱 表全國有3個字段:ID(主鍵),水平,NationID 的兩個表具有關係,其中全國相連。 NationID是外鍵,Nation.ID是主鍵。

並不奇怪?在我看來,NationAlly應該有一個名爲NationID1的字段,另一個名爲NationID2來創建一個國家和其他國家的「關係」。

我做錯了什麼?

+0

'NationAlly'應該代表一個多對多的關係嗎?即一個國家有許多盟友,也可以是許多國家的盟友? – 2011-03-24 17:34:47

+0

一個國家當然有很多NationAllies,因爲在國內有一個NationAlly實體的名單。一個單一的國家實體擁有一個且僅有的一個國家,即「所有者」國家(擁有NationAlly名單的國家)以及一個且僅有的一個國家,這是該國際關係所關注的「國家」。 – Francesco 2011-03-24 18:44:32

+0

什麼時候你會得到異常?你寫到你「創建一個新類型的民族對象」,調用SaveChanges並拋出異常。下面兩段你寫道:「我試圖用NationAllies字段保存一個國家空字段,這個異常不會拋出。」區別在哪裏?你在第一個案例中添加NationAllies? – Slauma 2011-03-24 19:17:39

回答

11

您可能是EF Code-First映射慣例的犧牲品,它會自動創建您不想擁有的NationAlliestoNation之間的關係。

如果我正確地理解了你(但我不是100%確定,如果我這樣做),你實際上想要有兩個關係,並且你只在每個實體中暴露關係的一端。因此,NationAllies不指向toNation,而是指向NationAlly實體中的「隱形」所有者國家。

如果是這種情況,您需要顯式覆蓋約定映射。在EF 4.1的流暢API,這可能是這樣的:

public class MyContext : DbContext 
{ 
    public DbSet<Nation> Nations { get; set; } 
    public DbSet<NationAlly> NationAllies { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Nation>() 
      .HasMany(n => n.NationAllies) 
      .WithRequired() 
      .Map(conf => conf.MapKey("OwnerID")) 
      .WillCascadeOnDelete(false); 

     modelBuilder.Entity<NationAlly>() 
      .HasRequired(a => a.toNation) 
      .WithMany() 
      .Map(conf => conf.MapKey("NationID")) 
      .WillCascadeOnDelete(false); 
    } 
} 

這種映射將創建兩個外鍵OwnerID並在NationAlliesNationID,都指向主鍵IDNations表。

編輯

這是我與測試的應用程序:

  • 在VS2010/.NET 4.0中創建一個新的控制檯應用程序,將其命名爲 「NationsApp」
  • 添加到參考「EntityFramework.dll」
  • 清除「Program.cs」的內容並粘貼代替以下內容:

程序內容。cs:

using System; 
using System.Collections.Generic; 
using System.Data.Entity; 

namespace NationsApp 
{ 
    public class Nation 
    { 
     public int ID { get; set; } 
     public int name { get; set; } 
     public List<NationAlly> NationAllies { get; set; } 
    } 

    public class NationAlly 
    { 
     public int ID { get; set; } 
     public int level { get; set; } 
     public Nation toNation { get; set; } 
    } 

    public class NationsContext : DbContext 
    { 
     public DbSet<Nation> Nations { get; set; } 
     public DbSet<NationAlly> NationAllies { get; set; } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      modelBuilder.Entity<Nation>() 
       .HasMany(n => n.NationAllies) 
       .WithRequired() 
       .Map(conf => conf.MapKey("OwnerID")) 
       .WillCascadeOnDelete(false); 

      modelBuilder.Entity<NationAlly>() 
       .HasRequired(a => a.toNation) 
       .WithMany() 
       .Map(conf => conf.MapKey("NationID")) 
       .WillCascadeOnDelete(false); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var context = new NationsContext()) 
      { 
       try 
       { 
        // We have three Nations and two Allies 
        Nation nation1 = new Nation() { 
         NationAllies = new List<NationAlly>() }; 
        Nation nation2 = new Nation() { 
         NationAllies = new List<NationAlly>() }; 
        Nation nation3 = new Nation() { 
         NationAllies = new List<NationAlly>() }; 
        NationAlly ally1 = new NationAlly(); 
        NationAlly ally2 = new NationAlly(); 

        // Nation1 has two Allies 
        // (Nation1 is the "owner" of both Allies) 
        nation1.NationAllies.Add(ally1); 
        nation1.NationAllies.Add(ally2); 

        // toNation of ally1 refers to Nation2 
        ally1.toNation = nation2; 
        // toNation of ally2 refers to Nation3 
        ally2.toNation = nation3; 

        context.Nations.Add(nation1); 
        context.Nations.Add(nation2); 
        context.Nations.Add(nation3); 

        context.SaveChanges(); 
       } 
       catch (Exception e) 
       { 
        throw; 
       } 
      } 
     } 
    } 
} 

您可以在「throw」上設置一個斷點,以在調試器中觀察e中的可能異常。

如果您使用SQL Server Express並且沒有定義任何其他連接字符串,將創建一個名爲NationsApp.NationsContext的數據庫。

它給出了兩個關係Nation_NationAllies(FK是「OwnerID」)和NationAlly_toNation(FK是「NationID」)。所有列都是不可空的。在DB結果如下:

Nations and NationAllies

+0

我想你真的理解我的問題,謝謝。我正在嘗試你的建議。但是即使我有一個System.Data.Entity的引用,VS2010也不允許我添加一個DbModelBuilder(被標記爲未知)。我錯過了什麼嗎? – Francesco 2011-03-24 21:07:03

+0

@Francesco:您使用的是哪一個EF代碼版本?從2010年12月開始,CTP4版本(2010年夏季),CTP5版本和最後一個版本從本月起被稱爲EF 4.1(發佈候選版本)(http://blogs.msdn.com/b/adonet/archive/2011/03/ 15/ef-4-1-release-candidate-available.aspx,這裏也有下載鏈接)。我的代碼基於最新版本。無論如何你都應該升級,沒有人會遲早會幫助你使用舊的初步版本。 – Slauma 2011-03-24 21:34:00

+0

而且您還需要對「EntityFramework.dll」的引用,而不僅僅是System.Data.Entity。 – Slauma 2011-03-24 21:41:49

6

如果這可以幫助別人得到這個錯誤,而這樣做的查詢,而不是保存到數據庫中...我得到這個消息。我的數據設計:

public class Base { 
    public int Id {get; set;} 
} 

public class Child { 
    [Key][ForeignKey("Base")] public int Id {get; set;} 

    public virtual Base Base {get; set;} 

    public Child() { 
     Base = new Base(); 
    } 
} 

問題出在構造函數中。結果EF4.1不喜歡當你初始化那裏的關聯!我刪除了構造函數,並且事情又開始了。