简体   繁体   English

实体框架,代码优先建模和循环引用

[英]Entity Framework, Code First modeling and a cyclical reference

I've spend several days now, trying to solve this problem. 我已经花了几天时间,试图解决这个问题。 While making a simple project to exemplify my problem, I stumbled upon a possible solution. 在做一个简单的项目来说明我的问题时,我偶然发现了一个可能的解决方案。 So, this is sort of a double question. 因此,这是一个双重问题。

But first, a little background info: 但首先,请提供一些背景信息:

I just started using Entity Framework 4.1 (EF) and Code First to create the models for my ASP.NET MVC project. 我刚刚开始使用Entity Framework 4.1(EF)和Code First为我的ASP.NET MVC项目创建模型。 I need some models similar to this: 我需要一些类似的模型:

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; }
    }
}

And the DbContext: 和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; }
    }
}

(Please excuse the lame example, that's what my Friday fried brain was able to come up with.) (请原谅这个la脚的例子,这就是我星期五的炒脑所能想到的。)

A family can have several mothers and several fathers. 一个家庭可以有几个母亲和几个父亲。 And a child has a mother and a father. 一个孩子有一个母亲和一个父亲。 I checked with one of the .NET gurus at my work, who agreed that there is nothing extraordinary in this. 我与工作中的一位.NET专家进行了核对,后者同意其中没有什么特别之处。 At least as far as we can see. 至少据我们所见。

But when I run the code, I get this Exception: 但是,当我运行代码时,出现以下异常:

System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. System.Data.SqlServerCe.SqlCeException:引用关系将导致不允许的循环引用。 [ Constraint name = Mother_Family ] [约束名称= Mother_Family]

I do see the cycle: Family - Mother - Child - Father - Family . 我确实看到了周期: Family - Mother - Child - Father - Family But if I created the database tables myself (which I prefer not to, that's what I like about Code First) it would be a perfectly valid data structure, as far as I can tell. 但是,如果我自己创建数据库表(我不喜欢这样做,那是我对Code First的喜欢),就我所知,这将是一个完全有效的数据结构。

So, my first question is: Why is this a problem when using code first? 所以,我的第一个问题是:为什么先使用代码时会出现问题? Is there a way to tell EF how to properly handle the cycle? 有没有办法告诉EF如何正确处理周期?

Then, as I write initially, while creating a simple project to exemplify my problem, I incidentally stumbled upon a possible solution. 然后,正如我最初编写的那样,在创建一个简单的项目来举例说明我的问题时,我偶然发现了一个可能的解决方案。 I simply forgot some of the properties when defining my models. 定义模型时,我只是忘记了一些属性。 For clarity in the following example, instead of removing them, I've commented out the parts of the models I forgot: 为了清楚起见,在下面的示例中,我没有删除它们,而是注释掉了我忘记的模型部分:

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; }
    }
}

So, removing these SomethingID reference properties seems to solve my problem. 因此,删除这些SomethingID参考属性似乎可以解决我的问题。 As you can see in the controller of the sample project I'm linking to in the end of this post, I'm still able to cycle all the way around and do stuff like mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name without any problems. 正如您在本文末尾链接到的示例项目的控制器中所看到的那样,我仍然可以一直循环浏览并执行诸如mothers.First().Family.Fathers.First().Children.First().Mother.Family.Name没有任何问题。 But all tutorials and examples about EF and Code First modeling I've been looking at (eg this one by Scott Guthrie ) include these properties, so it feels wrong not to use them. 但是我一直在研究的所有有关EF和Code First建模的教程和示例(例如Scott Guthrie的这篇文章 )都包含了这些属性,因此不使用它们会让人感到不对。

And so, my second question is: Will there be any drawbacks and problems I haven't discovered yet doing this? 因此,我的第二个问题是:这样做还不会发现任何弊端和问题吗?

Download example project here: http://blackfin.cannedtuna.org/cyclical-reference-test-app.zip , and open TestSolution.sln. 在此处下载示例项目: http ://blackfin.cannedtuna.org/cyclical-reference-test-app.zip,然后打开TestSolution.sln。 The properties are commented out in the example project. 这些属性在示例项目中被注释掉。 Uncomment the lines in TestModels.cs to add the properties, resulting in the cyclical reference exception. 取消注释TestModels.cs中的行以添加属性,从而导致循环引用异常。

NB: The solution is creating and seeding a SQL CE database located at c:\\TestApp.sdf 注意:解决方案是创建和播种位于c:\\ TestApp.sdf的SQL CE数据库

Update, December 2011: I never solved this problem technically, but I quit my job and found another job where I don't have to use Microsoft technologies. 更新,2011年12月:我从技术上从未解决过这个问题,但是我辞掉了工作,找到了另一项不必使用Microsoft技术的工作。 That sort of solved my problem :) 那种解决了我的问题:)

As the tech support at the old place used to write when fixing issues: "A workaround or solution has been provided". 在解决问题时,原为技术支持的地方是:“已提供解决方法或解决方案”。

But if I created the database tables myself (which I prefer not to, that's what I like about Code First) it would be a perfectly valid data structure, as far as I can tell. 但是,如果我自己创建数据库表(我不喜欢这样做,那是我对Code First的喜欢),就我所知,这将是一个完全有效的数据结构。

This is something you should double check. 您应该仔细检查这一点。 The exception comes directly from the database and not from Entity Framework. 异常直接来自数据库,而不是实体框架。 It's likely that also a table structure with the same constraints created by hand will be invalid. 具有手动创建的具有相同约束的表结构也可能无效。 Keep in mind that your foreign key properties Mother.FamilyID , Father.FamilyID , Child.MotherID and Child.FatherID are not nullable , so they represent required relationships and the corresponding columns in the database are also not nullable. 请记住,您的外键属性Mother.FamilyIDFather.FamilyIDChild.MotherIDChild.FatherID 不可为空 ,因此它们表示必需的关系,并且数据库中的相应列也不可为空。

When you remove all these properties from your model classes your relationships become suddenly optional because the navigation properties can be null . 当您从模型类中删除所有这些属性时,由于导航属性可以为null因此关系突然变得可选 This is another model now since the FK columns in the DB can be nullable! 现在这是另一个模型,因为数据库中的FK列可以为空! Apparently this is an allowed model. 显然,这是允许的模型。

If you want to have still foreign key properties in your model which represent optional instead of required relationship you can use nullable types: public int? FamilyID { get; set; } 如果您希望模型中仍然具有表示可选关系而非必需关系的外键属性,则可以使用可为空的类型: public int? FamilyID { get; set; } public int? FamilyID { get; set; } public int? FamilyID { get; set; } , public int? MotherID { get; set; } public int? FamilyID { get; set; }public int? MotherID { get; set; } public int? MotherID { get; set; } public int? MotherID { get; set; } , etc. public int? MotherID { get; set; }

我遇到了几乎相同的问题,但是我使用此答案中的建议解决了问题实体框架代码优先-来自同一表的两个外键比将键列的类型更改为可选更好。

This is a known problem and you're not the first to bump into it. 这是一个已知的问题,您不是第一个遇到此问题的人。 From what I've heard they are working on a better solution in the upcoming version of WCF, however for the time being from my experience you are much better off creating DataContracts that represent the data to be sent over the wire thereby changing the data structure to remove the cyclic reference. 据我所知,他们正在为即将到来的WCF版本中的更好的解决方案而努力,但是从我的经验来看,暂时最好创建DataContracts来表示要通过网络发送的数据,从而改变数据结构删除循环引用。

I know it's a pain, but there are other benefits to be had in that you most likely will want to make other changes to structures that your clients consume anyway instead of letting them play with the objects as they exist in your db 我知道这很痛苦,但是还有其他好处,因为您很可能希望对客户机无论如何都要使用的结构进行其他更改,而不是让它们玩数据库中存在的对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM