简体   繁体   English

实体框架:在表 '' 上引入 FOREIGN KEY 约束 '' 可能会导致循环或多个级联路径

[英]Entity Framework: Introducing FOREIGN KEY constraint '' on table '' may cause cycles or multiple cascade paths

While trying to implement the schema found on this answer with Entity Framework I get an error:在尝试使用 Entity Framework 实现在此答案中找到的架构时,出现错误:

Introducing FOREIGN KEY constraint 'FK_OptionValues_Products_ProductId' on table 'OptionValues' may cause cycles or multiple cascade paths在表“OptionValues”上引入 FOREIGN KEY 约束“FK_OptionValues_Products_ProductId”可能会导致循环或多个级联路径

+---------------+     +---------------+
| PRODUCTS      |-----< PRODUCT_SKUS  |
+---------------+     +---------------+
| #product_id   |     | #product_id   |
|  product_name |     | #sku_id       |
+---------------+     |  sku          |
        |             |  price        |
        |             +---------------+
        |                     |
+-------^-------+      +------^------+
| OPTIONS       |------< SKU_VALUES  |
+---------------+      +-------------+
| #product_id   |      | #product_id |
| #option_id    |      | #sku_id     |
|  option_name  |      | #option_id  |
+---------------+      |  value_id   |
        |              +------v------+
+-------^-------+             |
| OPTION_VALUES |-------------+
+---------------+
| #product_id   |
| #option_id    |
| #value_id     |
|  value_name   |
+---------------+

The model classes are currently like so model 类目前是这样的

public class Option
{
    public int Id { get; set; }

    [ForeignKey("ProductId")]
    public int ProductId { get; set; }

    public string OptionName { get; set; }
    public Product Product { get; set; }
}

public class OptionValue
{
    public int Id { get; set; }

    [ForeignKey("ProductId")]
    public int ProductId { get; set; }

    [ForeignKey("OptionId")]
    public int OptionId { get; set; }

    public string OptionValueName { get; set; }
    public Product Product { get; set; }
    public Option Option { get; set; }
}

public class ProductSku
{
    public int Id { get; set; }

    [ForeignKey("ProductId")]
    public int ProductId { get; set; }

    public string Sku { get; set; }
    public decimal Price { get; set; }
    public Product Product { get; set; }
}

public class SkuValue
{
    public int Id { get; set; }

    [ForeignKey("ProductId")]
    public int ProductId { get; set; }

    [ForeignKey("ProductSkuId")]
    public int ProductSkuId { get; set; }

    [ForeignKey("OptionId")]
    public int OptionId { get; set; }

    [ForeignKey("OptionValueId")]
    public int OptionValueId { get; set; }

    public Product Product { get; set; }
    public ProductSku ProductSku { get; set; }
    public Option Option { get; set; }
    public OptionValue OptionValue { get; set; }
}

What am I doing wrong here?我在这里做错了什么? How could I fix that?我怎么能解决这个问题?

This is because deleting a row from OptionValues will delete multiple rows from the other tables.这是因为从 OptionValues 中删除一行会从其他表中删除多行。 In MySQL you should not be getting an error, as i saw this happens a lot in SQL Server.在 MySQL 中,您应该不会收到错误,因为我看到这种情况在 SQL 服务器中经常发生。 Try adding:尝试添加:

.WillCascadeOnDelete(false); .WillCascadeOnDelete(false);

On your modelBuilder method.在您的模型构建器方法上。

This will not cascade delete all the other rows with the foreign key.这不会级联删除具有外键的所有其他行。

Theres a few competing things here, your model looks like it trying to use multiple FKs to ensure integrity, this doesn't match the general expectations of the default EF implementation, which just means you will have to do a lot of manual updates to individual fields as you insert and update them, and more importantly EF wont be able to identify the relationships correctly on its own, you will likely need to manage more of this in Fluent Configuration to get it right.这里有一些竞争的东西,您的 model 看起来像是在尝试使用多个 FK 来确保完整性,这与默认 EF 实现的一般预期不符,这意味着您将不得不对个人进行大量手动更新插入和更新字段时,更重要的是 EF 无法自行正确识别关系,您可能需要在Fluent Configuration中管理更多内容以使其正确。

The following statements show how I have interpreted your model:以下陈述显示了我如何解释您的 model:

  • a Product has many "Skus" - ProductSku一个Product有很多“Sku” - ProductSku
  • a Product has many "Options" - Option一个Product有很多“选项”—— Option
  • an Option has many named "values" - OptionValue一个Option有许多命名的“值” - OptionValue
  • each OptionValue can be linked to many "SKUs" - SkuValue每个OptionValue可以链接到许多“SKU” - SkuValue

If that is the case, then your diagram is not quite right and you class definitions should be changed.如果是这种情况,那么您的图表不太正确,您的 class 定义应该更改。

In general I would recommend the following:一般来说,我会推荐以下内容:

  1. SkuValue does not need an FK to Product, that is assumed from it's parent ProductSku SkuValue不需要产品的 FK,这是从它的父ProductSku假定的
  2. OptionValue also does not need an FK to Product as this is assumed from it's parent Option OptionValue也不需要Product的 FK,因为这是从它的父Option假设的
  3. SkuValue does not need an FK to Option as this is assumed from it's parent OptionValue SkuValue不需要FK 到Option ,因为这是从它的父OptionValue假设的

By leaving the FK to Product in each of these tables, EF is confused because it can see that by allowing these extra navigation links in the child tables, it is possible for your code to assign a different product say to an OptionValue than the Product that is defined in it's linked Option .通过在这些表中的每一个中将 FK 留给Product ,EF 感到困惑,因为它可以看到,通过在子表中允许这些额外的导航链接,您的代码可以为OptionValue分配不同的产品,而不是Product在其链接的Option中定义。

So while we often think that by leaving the fields there it will assist maintaining the integrity of the data, they make it easier for your code to violate it by creating links that should not normally be viable因此,虽然我们经常认为将字段留在那里有助于维护数据的完整性,但它们通过创建通常不可行的链接使您的代码更容易违反

It is possible to leave these fields in, but when the model has inconsistencies like this we need to add more configuration to get it across the line.可以保留这些字段,但是当 model 有这样的不一致时,我们需要添加更多配置以使其越界。

even with these changes, you will still have a bi-directional link between all these tables, as explained in this response: https://stackoverflow.com/a/65514280/1690217 you will need to remove one (or all) of the default cascade delete behaviours.即使进行了这些更改,您仍将在所有这些表之间建立双向链接,如此响应中所述: https://stackoverflow.com/a/65514280/1690217您将需要删除其中的一个(或全部)默认级联删除行为。


As a rule, I find it is safer to remove the Cascade Delete behaviour and manage it explicitly through fluent configurations, especially when your child records have deep repeated links.通常,我发现删除级联删除行为并通过流畅的配置显式管理它会更安全,尤其是当您的子记录具有深度重复链接时。

The default conventions in EF work reasonably well as long as you stick to simple models, only use single keys in tables, don't duplicate key references to child items that are defined in the parent records, and don't form any child relationships with records that this table is already a child of. EF 中的默认约定工作得相当好,只要您坚持简单的模型,只在表中使用单个键,不复制对父记录中定义的子项的键引用,并且不与记录该表已经是其子表。

If you want to remove the default cascade delete behaviour, I recommend this line in your OnModelCreating method:如果您想删除默认的级联删除行为,我建议您在OnModelCreating方法中使用这一行:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

If you haven't removed this convention, then you will need to use Fluent Notation to configure the ForeignKey , even though you have already attempted to use the Attribute Notation如果您尚未删除此约定,那么您将需要使用Fluent Notation来配置ForeignKey ,即使您已经尝试使用Attribute Notation

In Fluent notation, you can specify the cascade behaviour and either enable or disable it on individual keys, Fluent configuration of foreign keys will override defined attributes for the same key fields.在 Fluent 表示法中,您可以指定级联行为并在单个键上启用或禁用它,外键的流畅配置将覆盖相同键字段的定义属性。
See Cascade Delete in the EF Core docs请参阅EF Core 文档中的级联删除

modelBuilder.Entity<Product>().HasMany(p => p.ProductsSkus).WithOne(sku => sku.Product).HasForeignKey(sku => sku.ProductId).OnDelete(DeleteBehavior.Restrict);

Regarding correct use of ForeignKeyAttribute关于正确使用ForeignKeyAttribute

Your use of the [ForeignKey] attribute is non compliant, so you must already be overriding this in fluent notation.您对[ForeignKey]属性的使用不符合要求,因此您必须已经用流利的表示法覆盖了它。 When annotating a Navigation property, use ForeignKeyAttribute to describe the name of the FK property.注释Navigation属性时,使用ForeignKeyAttribute来描述FK属性的名称。 You can also use it in the reverse manner, you can place the attribute on the FK property but not it is used to describe the Navigation property.也可以反过来使用,可以将属性放在FK属性上,但不能用于描述Navigation属性。

Notice the subtle difference:注意细微的差别:

public class Option
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public string OptionName { get; set; }
    [ForeignKey("ProductId")]
    public Product Product { get; set; }
}

or this way:或者这样:

public class Option
{
    public int Id { get; set; }
    [ForeignKey("Product")]
    public int ProductId { get; set; }
    public string OptionName { get; set; }
    public Product Product { get; set; }
}

But take this the next step, get rid of magic strings and use a compile-time safe reference to the matching property:但是下一步,摆脱魔术字符串并使用对匹配属性的编译时安全引用:

public class Option
{
    public int Id { get; set; }
    [ForeignKey(nameof(Option.Product))]
    public int ProductId { get; set; }
    public string OptionName { get; set; }
    public Product Product { get; set; }
}

In this way you get more of an understanding of the importance that the value passed into the FK attribute is an actual reference to another field.通过这种方式,您可以更多地了解传递给 FK 属性的值是对另一个字段的实际引用的重要性。 Remember that when using attribute notation there is no support currently to specify the cascade behaviour of a FK, so make sure you enable or disable the convention that applies cascade delete automatically so that you do not have to define every FK using Fluent Notation .请记住,使用属性表示法时,当前不支持指定 FK 的级联行为,因此请确保启用或禁用自动应用级联删除的约定,这样您就不必使用Fluent Notation定义每个 FK。

Ultimately the following class definition (for the snippet you have shown) should work, just remove any fluent notation that might be overriding this:最终,以下 class 定义(对于您显示的代码段)应该可以工作,只需删除任何可能覆盖它的流利表示法:

Note that I have also defined the navigation links from the parent records to access the subordinate records, this allows your Linq queries to traverse in either direction through the navigation links without having to use joining syntax.请注意,我还定义了来自父记录的导航链接以访问从属记录,这允许您的Linq查询通过导航链接在任一方向遍历,而无需使用连接语法。

public class Product
{
    public int Id { get; set; }
    ...
    public virtual ICollection<Option> Options { get; set; } = new HashSet<Option>();
    public virtual ICollection<ProductSku> Skus { get; set; } = new HashSet<ProductSku>();
}

public class Option
{
    public int Id { get; set; }
    [ForeignKey(nameof(Option.Product))]
    public int ProductId { get; set; }

    public string OptionName { get; set; }
    public Product Product { get; set; }

    public virtual ICollection<OptionValue> Values { get; set; } = new HashSet<OptionValue>();
}

public class OptionValue
{
    public int Id { get; set; }
    [ForeignKey(nameof(OptionValue.Option))]
    public int OptionId { get; set; }

    public string OptionValueName { get; set; }
    public Option Option { get; set; }

    public virtual ICollection<SkuValue> Skus { get; set; } = new HashSet<SkuValue>();
}

public class ProductSku
{
    public int Id { get; set; }
    [ForeignKey(nameof(ProductSku.Product))]
    public int ProductId { get; set; }

    public string Sku { get; set; }
    public decimal Price { get; set; }
    public Product Product { get; set; }

    public virtual ICollection<SkuValue> Options { get; set; } = new HashSet<SkuValue>();
}

// many : many link between ProductSku and OptionValue
public class SkuValue
{
    public int Id { get; set; }
    [ForeignKey(nameof(SkuValue.ProductSku))]
    public int ProductSkuId { get; set; }
    [ForeignKey(nameof(SkuValue.OptionValue))]
    public int OptionValueId { get; set; }

    public ProductSku ProductSku { get; set; }
    public OptionValue OptionValue { get; set; }
}

Try to remove from OptionValue and SkuValue class尝试从 OptionValue 和 SkuValue class 中删除

 [ForeignKey("ProductId")]
  public int ProductId { get; set; }

since you have already ProductId in Option and ProductSku class因为您已经在 Option 和 ProductSku class 中有 ProductId

The problem here is how the foreign key is declared.这里的问题是如何声明外键。 On SQL you can se a Foreign Key with the "ON UPDATE" and "ON DELETE" arguments as "CASCADE" or "NO ACTION".在 SQL 上,您可以将“ON UPDATE”和“ON DELETE”arguments 设置为“CASCADE”或“NO ACTION”的外键。

The "NO ACTION" would not cause that error, but the "CASCADE" will, since it will interact with the row, and all its references that would (or better yet, could) cause a ciclic reference, for example, delete on OptionsValue could cascade to a delete in Options, and so, cascade to a delete in Products, due to it cause a delete on ProductsSku that would also delete SkusValues (that refer to produts also, so cascade again...). “NO ACTION”不会导致该错误,但“CASCADE”会,因为它将与该行交互,并且它的所有引用会(或者更好的是,可能)导致 ciclic 引用,例如,在 OptionsValue 上删除可以级联到选项中的删除,因此,级联到产品中的删除,因为它会导致 ProductsSku 上的删除也会删除 SkusValues(也指产品,所以再次级联......)。

If all is set (or at least the "parent" tables) to "NO ACTION", then deleting a row would only occur if there's no reference for it on the other tables, and would be safe to use.如果 all (或至少“父”表)设置为“NO ACTION”,那么只有在其他表上没有引用它时才会删除一行,并且可以安全使用。 So on your declarations you need to set that cascade options to false (equivalent to the "NO ACTION" on the sql statement that is generated).因此,在您的声明中,您需要将该级联选项设置为 false(相当于生成的 sql 语句上的“NO ACTION”)。

暂无
暂无

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

相关问题 SQL 错误:引入 FOREIGN KEY 约束可能会导致循环或多个级联路径。 实体框架核心 - SQL Error: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths. Entity Framework Core 引入 FOREIGN KEY 约束...可能导致循环或多个级联路径 - 实体框架 - Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths - Entity Framework 实体框架C#在表Y中引入外键X可能会导致循环或多个级联路径 - Entity Framework C# Introducing A Foreign Key X In Table Y May Cause Cycles OR Multiple Cascade Paths ef核心2-在表&#39;Y&#39;上引入FOREIGN KEY约束&#39;X&#39;可能会导致循环或多个级联路径 - ef core 2 - Introducing FOREIGN KEY constraint 'X' on table 'Y' may cause cycles or multiple cascade paths 在表上引入外键约束可能会导致循环或多个级联路径 - Introducing Foreign key Constraint on table may cause cycles or multiple cascade paths 在表“模型”上引入FOREIGN KEY约束“列”可能会导致循环或多个级联路径 - Introducing FOREIGN KEY constraint 'Column' on table 'Model' may cause cycles or multiple cascade paths 表&#39;UsageSummaries&#39;上的多态与引入FOREIGN KEY约束可能会导致循环或多个级联路径 - Polymorphism vs Introducing FOREIGN KEY constraint '' on table 'UsageSummaries' may cause cycles or multiple cascade paths 在表table上引入FOREIGN KEY约束键可能会导致循环或多个级联路径。 指定ON DELETE…错误 - Introducing FOREIGN KEY constraint key on table table may cause cycles or multiple cascade paths. Specify ON DELETE … Error 实体框架,外键约束可能会导致循环或多个级联路径 - Entity Framework, Foreign key constraint may cause cycles or multiple cascade paths 当不需要属性时,引入外键约束可能会导致循环或多个级联路径 - Introducing Foreign Key Constraint may cause cycles or multiple cascade paths when property is not required
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM