【问题标题】:Entity Framework: how to solve "FOREIGN KEY constraint may cause cycles or multiple cascade paths"?实体框架:如何解决“外键约束可能导致循环或多个级联路径”?
【发布时间】:2013-01-07 12:29:15
【问题描述】:

关于这个问题有很多问题,但我无法解决我的问题。 有人可以看看这个吗:

我有一个Office 表,它与DoctorSecretary 表有一对多的关系。最后两个表都派生自Employee 表,该表与sqlmembershipprovider 创建的预定义Users 表具有共享主键关系。 Users table 和 Roles table 之间似乎存在多对多关系,我没有参与其中。

我的问题是在我的Employee 表和Users 表之间创建(零,一)-(一)关系,我以它们之间的共享主键关系结束,然后引发错误。 (这个问题有更好的解决方案吗?)

这是错误:

引入 FOREIGN KEY 约束 表上的“FK_dbo.aspnet_UsersInRoles_dbo.aspnet_Users_UserId” “aspnet_UsersInRoles”可能会导致循环或多个级联路径。 指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 外键约束。无法创建约束。见上 错误。

这是我在逆向工程后的代码和会员代码:

public class Office
{
    public Office()
    {
        this.Doctors = new HashSet<Doctor>();
        this.Secretaries = new HashSet<Secretary>();
    }

    [Key]
    public System.Guid OfficeId { get; set; }
    public virtual ICollection<Doctor> Doctors { get; set; }
    public virtual ICollection<Secretary> Secretaries { get; set; }
}

public class Employee
{
    [Key, ForeignKey("User")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public System.Guid Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("Office")]
    public System.Guid OfficeId { get; set; }

    // shared primary key 
    public virtual aspnet_Users User { get; set; }

    public virtual Office Office { get; set; }
}

public class Doctor :Employee
{
    public Doctor()
    {
        this.Expertises = new HashSet<Expertise>();
    }
    //the rest..    
    public virtual ICollection<Expertise> Expertises { get; set; }
}

public class Secretary : Employee
{
    // blah blah
}

public class aspnet_Users
{
    public aspnet_Users()
    {
        this.aspnet_Roles = new List<aspnet_Roles>();
    }

    public System.Guid ApplicationId { get; set; }
    public System.Guid UserId { get; set; }
    //the rest..
    public virtual aspnet_Applications aspnet_Applications { get; set; }
    public virtual ICollection<aspnet_Roles> aspnet_Roles { get; set; }
}

public class aspnet_Roles
{
    public aspnet_Roles()
    {
        this.aspnet_Users = new List<aspnet_Users>();
    }

    public System.Guid ApplicationId { get; set; }
    public System.Guid RoleId { get; set; }
    //the rest..
    public virtual aspnet_Applications aspnet_Applications { get; set; }
    public virtual ICollection<aspnet_Users> aspnet_Users { get; set; }
}

编辑:关系更深,Users 表和Applications 表之间存在多对一关系,RolesApplications 之间也存在。

【问题讨论】:

  • 让我说对了,您是否对 Employee 和 Users 表中的 PK 使用相同的值?
  • 是的,因为我希望首先应该有一个用户价值,然后我在 Employee 中使用它。该错误仅在初始化时引发。

标签: asp.net entity-framework sqlmembershipprovider


【解决方案1】:

您可以使用 fluent api 指定错误消息建议的操作。

在您的上下文中:

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<aspnet_UsersInRoles>().HasMany(i => i.Users).WithRequired().WillCascadeOnDelete(false);
}

请注意,您没有包含表 aspnet_UsersInRoles 的定义,因此此代码可能不起作用。

另一个选项是通过添加这个来删除所有 CASCADE DELETES

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

如果您需要更多关于使用 fluent api 配置关系的信息,我建议http://msdn.microsoft.com/en-US/data/jj591620

【讨论】:

  • 你好,查尔斯。我无法操纵UsersRolesUsersInRoles,因为会员提供程序将停止工作。我可以更改表格中某处的圆圈吗?
  • 嗯 EF 正在尝试修改这些表。您的错误来自 EF 尝试添加外键约束。尝试将 [NotMapped] 添加到每个 aspnet_* 类定义中。这将允许您使用这些类,但 EF 不会尝试为它们创建表。
  • 注意:如果使用此覆盖方法,请记住也调用base.OnModelCreating(modelBuilder);,否则不会设置其他约定默认值。对我来说,这导致了新的错误,例如EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
  • 如果您使用数据库优先设计怎么办?这个答案仍然适用吗?
【解决方案2】:

您还可以修改迁移类。在我的迁移课程中是:

CreateTable(
    "dbo.Spendings",
    c => new
        {
          SpendingId = c.Int(nullable: false, identity: true),
          CategoryGroupId = c.Int(nullable: false),
          CategoryId = c.Int(nullable: false),
          Sum = c.Single(nullable: false),
          Date = c.DateTime(nullable: false),
          Comment = c.String(),
          UserId = c.String(),
          LastUpdate = c.String(),
        })
    .PrimaryKey(t => t.SpendingId)
    .ForeignKey("dbo.Categories", t => t.CategoryId, cascadeDelete: true)
    .ForeignKey("dbo.CategoryGroups", t => t.CategoryGroupId, cascadeDelete: true)
    .Index(t => t.CategoryGroupId)
    .Index(t => t.CategoryId);

删除“cascadeDelete: true”后,Update-Database 运行良好。

false

 .ForeignKey("dbo.Categories", t => t.CategoryId, cascadeDelete: false)

【讨论】:

    【解决方案3】:
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        }
    

    在上下文中添加这段代码

    【讨论】:

    • 我要使用这个吗?它会产生什么样的负面影响?
    • 这是我有时使用的解决方案,但这是因为我很少想要级联删除任何东西 - 我发现它可能很危险。但是,有些人可能想要级联删除某些东西(它在正确的情况下可能很有用),因此负面影响是您必须先删除与/使用特定外键关联的每个项目,然后才能删除项目本身。如果您仍然希望将某些内容级联删除,则可能需要做一些更像“最佳答案”的事情。
    【解决方案4】:

    根据您的 cmets,问题在于您在两个表中使用相同的 PK。 我会立即改变这一点,将用户 PK 作为员工中的一个额外字段,与用户有 FK 关系(我猜是一对一)。并为用户添加一个独立的 PK(另一个尊重您的表格的指南)。如果要保证Employees 中的新外键(指向Users 的外键)是唯一的,可以随时添加唯一索引。

    这样你的结构保持不变,你摆脱了循环。

    【讨论】:

      【解决方案5】:

      只是一个更新

      正如其他答案所暗示的,您无需对删除执行任何操作。 Entityframework Core 1.0 的更新方法如下

      table.ForeignKey(name: "FK_Cities_Regions_RegionId",
                       column: x => x.RegionId,
                       principalTable: "Regions",
                       principalColumn: "RegionId",
                       onDelete: ReferentialAction.NoAction);
      

      注意:在onDelete 上执行ReferentialAction.NoAction

      【讨论】:

        猜你喜欢
        • 2010-10-25
        • 1970-01-01
        • 2019-06-06
        • 2012-09-21
        • 1970-01-01
        相关资源
        最近更新 更多