我正在努力解决一个非常乏味的问题。 我有一个名为Nation的类和一个名为NationAlly的类
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以及一个名为NationsDB的DbContext来管理SQL Server 2008上的数据库。 如果我创建一个Nation类型的新对象,并尝试调用nationDB.SaveChanges,我得到以下异常:
“违反了多重约束。”CodeFirstNamespace.NationAlly_toNation'关系的'NationAlly_toNation_Target'角色具有多重性1或0..1。“
我试图用NationAllies字段保存一个Nation,并且不抛出此异常,数据库中的country表获取所有正确的值。
在我的数据库中,表Nation有2个字段:ID(主键),名称 表NationAlly有3个字段:ID(主键),级别,NationID 这两个表链接的关系是NationAlly.NationID是外键,Nation.ID是主键。
不奇怪吗?在我看来,NationAlly表应该有一个名为NationID1的字段,另一个叫做NationID2,以创建一个国家和其他国家名单之间的“关系”。
我做错了什么?
答案 0 :(得分:11)
您可能是EF Code-First映射约定的受害者,这些约定会自动创建您不希望拥有的NationAllies
和toNation
之间的关系。
如果我理解正确(但我不是百分之百确定,如果我这样做),你实际上想要有两个关系,并且你只在每个实体中暴露了关系的一端。因此,NationAllies
并未指向toNation
,而是指向NationAlly
实体中的“隐身”所有者国家。
如果是这种情况,则需要显式覆盖约定映射。在EF 4.1的Fluent 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
表中创建两个外键NationID
和NationAllies
,两者都指向ID
表中的主键Nations
修改强>
以下是我测试过的应用程序:
Program.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中的结果如下:
答案 1 :(得分: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不喜欢!我删除了那个构造函数,事情又开始了。