我现在花了几天时间,试图解决这个问题。在制作一个简单的项目来举例说明我的问题时,我偶然发现了一个可能的解决方案。所以,这是一个双重问题。
但首先,一些背景信息:
我刚开始使用Entity Framework 4.1(EF)和Code First为我的ASP.NET MVC项目创建模型。我需要一些类似的模型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestApp.Models
{
public class Family
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Father> Fathers { get; set; }
public virtual ICollection<Mother> Mothers { get; set; }
}
public class Mother
{
public int ID { get; set; }
public string Name { get; set; }
public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Father
{
public int ID { get; set; }
public string Name { get; set; }
public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Child
{
public int ID { get; set; }
public string Name { get; set; }
public int MotherID { get; set; }
public int FatherID { get; set; }
public virtual Mother Mother { get; set; }
public virtual Father Father { get; set; }
}
}
和DbContext:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace TestApp.Models
{
public class TestContext : DbContext
{
public DbSet<Family> Families { get; set; }
public DbSet<Mother> Mothers { get; set; }
public DbSet<Father> Fathers { get; set; }
public DbSet<Child> Children { get; set; }
}
}
(请原谅这个蹩脚的例子,这就是我星期五油炸大脑能够想到的。)
一个家庭可以有几个母亲和几个父亲。一个孩子有一个母亲和一个父亲。我在我的工作中与一位.NET专家进行了核实,他们同意在这方面没有什么特别之处。至少就我们所见。
但是当我运行代码时,我得到了这个例外:
System.Data.SqlServerCe.SqlCeException:引用关系将导致不允许循环引用。 [约束名称= Mother_Family]
我确实看到了周期:Family - Mother - Child - Father - Family
。但是,如果我自己创建数据库表(我不喜欢这样,那就是我喜欢的Code First),就我所知,它将是一个完全有效的数据结构。
所以,我的第一个问题是:为什么在首先使用代码时这是一个问题?有没有办法告诉EF如何正确处理循环?
然后,正如我最初写的那样,在创建一个简单的项目来举例说明我的问题时,偶然发现了一个可能的解决方案。我在定义模型时忘记了一些属性。为了清楚起见,在下面的示例中,我没有删除它们,而是注释掉了我忘记的模型部分:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestApp.Models
{
public class Family
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Father> Fathers { get; set; }
public virtual ICollection<Mother> Mothers { get; set; }
}
public class Mother
{
public int ID { get; set; }
public string Name { get; set; }
// public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Father
{
public int ID { get; set; }
public string Name { get; set; }
// public int FamilyID { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual Family Family { get; set; }
}
public class Child
{
public int ID { get; set; }
public string Name { get; set; }
// public int MotherID { get; set; }
// public int FatherID { get; set; }
public virtual Mother Mother { get; set; }
public virtual Father Father { get; set; }
}
}
因此,删除这些SomethingID
引用属性似乎可以解决我的问题。正如您在本文结尾处链接到的示例项目的控制器中所看到的那样,我仍然能够一直循环并执行mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name
之类的操作而没有任何问题。但是我所关注的EF和Code First建模的所有教程和示例(例如this one by Scott Guthrie)都包含这些属性,因此不使用它们感觉不对。
所以,我的第二个问题是:我还没有发现任何缺点和问题吗?
在此处下载示例项目:http://blackfin.cannedtuna.org/cyclical-reference-test-app.zip,然后打开TestSolution.sln。这些属性在示例项目中被注释掉。取消注释TestModels.cs中的行以添加属性,从而产生循环引用异常。
NB:解决方案是创建并播种位于c:\ TestApp.sdf的SQL CE数据库
更新,2011年12月: 我从来没有在技术上解决这个问题,但我辞掉了工作,找到了另一份工作,我不必使用Microsoft技术。那种解决了我的问题:)
由于旧地方的技术支持在修复问题时曾经写过:“已提供解决方法或解决方案”。
答案 0 :(得分:3)
但如果我自己创建数据库表(我不喜欢, 这就是我喜欢的Code First)它将是完全有效的 数据结构,据我所知。
这是你应该仔细检查的东西。该异常直接来自数据库,而不是来自Entity Framework。也可能是手工创建的具有相同约束的表结构也是无效的。请注意,您的外键属性Mother.FamilyID
,Father.FamilyID
,Child.MotherID
和Child.FatherID
不可为空,因此它们代表所需的关系和相应的数据库中的列也不可为空。
当您从模型类中删除所有这些属性时,您的关系会突然变为可选,因为导航属性可以是null
。这是另一个模型,因为DB中的FK列可以为空!显然这是一个允许的模型。
如果您希望模型中仍有外键属性表示可选而非必需的关系,则可以使用可为空的类型:public int? FamilyID { get; set; }
,public int? MotherID { get; set; }
等。
答案 1 :(得分:0)
这是一个众所周知的问题,你并不是第一个碰到它的人。据我所知,他们正在为即将推出的WCF版本提供更好的解决方案,但是从我的经验来看,您最好创建表示通过线路发送的数据的DataContracts,从而改变数据结构删除循环引用。
我知道这很痛苦,但还有其他一些好处,你很可能会想要对客户消费的结构进行其他更改,而不是让他们玩你的数据库中存在的对象< / p>
答案 2 :(得分:0)
我遇到了同样的问题但是我使用这个答案Entity Framework Code First - two Foreign Keys from same table中的建议解决了这个问题,这比将密钥列的类型更改为可选项更好。