2011-03-24 158 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不喜欢当你初始化那里的关联!我删除了构造函数,并且事情又开始了。