.Net实体框架循环级联路径

时间:2015-04-09 04:02:04

标签: c# .net sql-server asp.net-mvc

我在使用C#Entity Framework创建表之间的外键关系时遇到了一些问题。我有这些表:

CREATE TABLE [dbo].[Course] (
    [Id]                 INT           IDENTITY (1, 1) NOT NULL,,
    CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED ([ID] ASC)
);

CREATE TABLE [dbo].[PreqEdge] (
    [Id]          INT IDENTITY (1, 1) NOT NULL,
    [ParentID] INT NOT NULL,
    [ChildID]   INT NOT NULL,
    CONSTRAINT [PK_PreqEdge] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dependant] FOREIGN KEY ([ParentID]) REFERENCES [dbo].[Course] ([Id]),
    CONSTRAINT [FK_Depends] FOREIGN KEY ([ChildID]) REFERENCES [dbo].[Course] ([Id])
);

相应的模型如下:

public partial class Course
{
    public int Id { get; set; }

    public virtual ICollection<PreqEdge> Parents { get; set; }
    public virtual ICollection<PreqEdge> Children { get; set; }

    public Course()
    {
        Parents = new HashSet<PreqEdge>();
        Children = new HashSet<PreqEdge>();
    }
}

public partial class PreqEdge
{
    public int Id { get; set; }
    public int ParentID { get; set; }
    public int ChildID { get; set; }

    public virtual Course Parent { get; set; }
    public virtual Course Child { get; set; }
}

每当我使用我的数据库上下文访问它时,我都会收到此错误:

介绍FOREIGN KEY约束&#39; FK_dbo.PreqEdges_dbo.Courses_ChildID&#39;在桌子上&#39; PreqEdges&#39;可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

这些表的目的是将两个课程连接在一起,就像图形的边缘一样,以便为该课程创建一系列先决条件,根据您是按ChildID还是ParentID进行过滤,可以向前或向后遍历。 / p>

据我所知,Entity Framework默认情况下对引用使用级联删除,我可以通过覆盖DbContext中的OnModelCreating方法来解决这个问题,但是我不知道如何使用DbModelBuilder这样指定可以通过该表中的两个键中的任何一个键对表进行外键控制的关系。

在没有手动编写SQL调用的情况下,是否可以在Entity Framework中执行此操作?

编辑:为了清晰起见,将DependantID和DependsID更改为ParentID和ChildID

3 个答案:

答案 0 :(得分:3)

您必须使用您的模型设置检查循环引用。在这个模型中,每门课程都可以与每门课程相连。如果可能的话,以某种方式降低模型的灵活性可能更容易。

以下是对象模型,作为您可以获得的各种循环引用情况的示例:

Object model

灵感如何降低循环参考的机会:

Class Model

您可以按课程类型将课程划分为多个组,例如SpecializedCourse不能成为任何课程的家长,但可以成为GeneralCourse的孩子。

答案 1 :(得分:1)

我找到了link的正确解决方案。对于这个例子,你会做这样的事情:

public class Course
{
    public int Id { get; set; }
    public string CouresNumber { get; set; }

    public virtual ICollection<PreqEdge> Parents { get; set; }
    public virtual ICollection<PreqEdge> Children { get; set; }
}

public class PreqEdge
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public int ChildId { get; set; }

    public virtual Course Parent { get; set; }
    public virtual Course Child { get; set; }
}

public class CourseContext : DbContext
{
    public DbSet<Course> Courses { get; set; }
    public DbSet<PreqEdge> PreqEdges { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PreqEdge>()
                    .HasRequired(e => e.Parent)
                    .WithMany(c => c.Parents)
                    .HasForeignKey(e => e.ParentId)
                    .WillCascadeOnDelete(false);

        modelBuilder.Entity<PreqEdge>()
                    .HasRequired(e => e.Child)
                    .WithMany(c => c.Children)
                    .HasForeignKey(e => e.ChildId)
                    .WillCascadeOnDelete(false);

        base.OnModelCreating(modelBuilder);
    }
}

我犯的另一个错误是首先尝试混合代码和数据库。

答案 2 :(得分:1)

尝试两种方法。

1) set lazy loading off in constructor of DbContext class

   this.Configuration.LazyLoadingEnabled = false;

It will not allow to load referencing objects

2) use [notmapped] on PreqEdge referencing properties as 

[notmapped]    
public virtual Course Parent { get; set; }
[notmapped]
public virtual Course Child { get; set; }

This is just to break the cycle.