实体框架,Code First建模和循环引用

时间:2011-08-08 12:31:42

标签: c# entity-framework-4.1 code-first modeling

我现在花了几天时间,试图解决这个问题。在制作一个简单的项目来举例说明我的问题时,我偶然发现了一个可能的解决方案。所以,这是一个双重问题。

但首先,一些背景信息:

我刚开始使用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技术。那种解决了我的问题:)

由于旧地方的技术支持在修复问题时曾经写过:“已提供解决方法或解决方案”。

3 个答案:

答案 0 :(得分:3)

  

但如果我自己创建数据库表(我不喜欢,   这就是我喜欢的Code First)它将是完全有效的   数据结构,据我所知。

这是你应该仔细检查的东西。该异常直接来自数据库,而不是来自Entity Framework。也可能是手工创建的具有相同约束的表结构也是无效的。请注意,您的外键属性Mother.FamilyIDFather.FamilyIDChild.MotherIDChild.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中的建议解决了这个问题,这比将密钥列的类型更改为可选项更好。