简体   繁体   English

EF Core——复杂的关系

[英]EF Core – Complicated Relationships

I have complicated relationships between these entities:我在这些实体之间有复杂的关系:

  • Country国家
  • Airport飞机场
  • Airline航空公司
  • Flight飞行

Country has many Airlines and many Airports.国家有许多航空公司和许多机场。 Airline has one Countries, the same about Airport.航空公司有一个国家,机场也一样。 Airline has many Flights, Airport has many DepartureFlights and ArrivalFlights (both are Flight type).航空公司有很多航班,机场有很多 DepartureFlights 和 ArrivalFlights(都是航班类型)。 Flight has one Airline and one DepartureAirport and one ArrivalAirport (both are Airport type).航班有一家航空公司和一个出发机场和一个到达机场(均为机场类型)。

Country can have no airlines and airports, Airline can have no Flights, Airport can have neither DepartureFlights nor ArrivalFlights. Country 可以没有航空公司和机场,Airline 可以没有 Flights,Airport 可以既没有 DepartureFlights 也没有 ArrivalFlights。

What I am trying to do is when Country is deleted, then all related Airlines and Airports are deleted, also when Airline or DepartureAirport or ArrivalAirport are deleted, all related Flights are deleted also, but when updating my db after the migration is created I'm getting an error: Introducing FOREIGN KEY constraint "FK_Flights_Airports_DepartureAirport" on table "Flights" may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.我想做的是当国家被删除时,所有相关的航空公司和机场都被删除,当航空公司或出发机场或到达机场被删除时,所有相关的航班也被删除,但是在创建迁移后更新我的数据库时我'我得到一个错误: Introducing FOREIGN KEY constraint "FK_Flights_Airports_DepartureAirport" on table "Flights" may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Introducing FOREIGN KEY constraint "FK_Flights_Airports_DepartureAirport" on table "Flights" may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

How to implement this behavior and prevent an error?如何实现此行为并防止错误?

Here are my models:这是我的模型:

  1. Country:国家:
public class Country
    {
        public int Id { get; set; }

        /* other properties */

        public virtual ICollection<Airport>? Airports { get; set; }

        public virtual ICollection<Airline>? Airlines { get; set; }
    }
  1. Airline:航空公司:
public class Airline
    {
        public int Id { get; set; }

        /* other properties */

        public int CountryId { get; set; }

        public virtual Country Country { get; set; }

        public virtual ICollection<Flight>? Flights { get; set; }
    }
  1. Airport:飞机场:
public class Airport
    {
        public int Id { get; set; }

        public string Name { get; set; }

        /* other properties */

        public int CountryId { get; set; }

        public virtual Country Country { get; set; }

        public virtual ICollection<Flight>? DepartureFlights { get; set; }

        public virtual ICollection<Flight>? ArrivalFlights { get; set; }
    }
  1. Flight:飞行:
public class Flight
    {
        public int Id { get; set; }

        /* other properties */

        public int AirlineId { get; set; }

        public int DepartureAirportId { get; set; }

        public int ArrivalAirportId { get; set; }

        public virtual Airline Airline { get; set; }

        public virtual Airport DepartureAirport { get; set; }

        public virtual Airport ArrivalAirport { get; set; }
    }

After all the DBContext file:在所有 DBContext 文件之后:

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Airline> Airlines { get; set; }
    public DbSet<Airport> Airports { get; set; }
    public DbSet<Country> Countries { get; set; }
    public DbSet<Flight> Flights { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Airline>(x =>
        {
            x.HasKey(a => a.Id);

            x.HasOne(c => c.Country)
            .WithMany(a => a.Airlines)
            .HasForeignKey(a => a.CountryId)
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();
        });

        modelBuilder.Entity<Airport>(x =>
        {
            x.HasKey(a => a.Id);

            x.HasOne(c => c.Country)
            .WithMany(a => a.Airports)
            .HasForeignKey(a => a.CountryId)
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();
        });

        modelBuilder.Entity<Country>(x =>
        {
            x.HasKey(c => c.Id);
        });

        modelBuilder.Entity<Flight>(x =>
        {
            x.HasKey(f => f.Id);

            x.HasOne(a => a.Airline)
            .WithMany(f => f.Flights)
            .HasForeignKey(f => f.AirlineId)
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();

            x.HasOne(a => a.DepartureAirport)
            .WithMany(f => f.DepartureFlights)
            .HasForeignKey(f => f.DepartureAirportId)
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();

            x.HasOne(a => a.ArrivalAirport)
            .WithMany(f => f.ArrivalFlights)
            .HasForeignKey(f => f.ArrivalAirportId)
            .OnDelete(DeleteBehavior.Cascade)
            .IsRequired();
        });
    }
}

How to implement this behavior and prevent an error?如何实现此行为并防止错误?

在此处输入图像描述

This is officially known issue . 这是官方已知的问题 Therefore, We have two ways to handle this scenario thus the error:因此,我们有两种方法来处理这种情况,从而导致错误:

1. Change one or more of the relationships to not cascade delete. 1.将其中一个或多个关系改为不级联删除。

In this scenario, we could make the Country relationship with Airport , Flight and Airlines optional by giving it a nullable foreign key property: for instance we can do something like:在这种情况下,我们可以通过为 Airport 、 Flight 和 Airlines 提供一个可为空的外键属性,使CountryAirportFlightAirlines的关系成为可选的:例如,我们可以这样做:

.IsRequired(false);

Note: You can check our official document for more details.注意:您可以查看我们的官方文档以获取更多详细信息。

2. Configure the database without one or more of these cascade deletes, then ensure all dependent entities are loaded so that EF Core can perform the cascading behavior. 2. 在没有这些级联删除中的一个或多个的情况下配置数据库,然后确保加载所有依赖实体,以便 EF Core 可以执行级联行为。

Considering this appreach we can keep the Airport , Flight and Airlines relationship required and configured for cascade delete, but make this configuration only apply to tracked entities, not the database: So we can do somethng like below:考虑到这个方法,我们可以保留级联删除所需和配置的AirportFlightAirlines关系,但使此配置仅适用于跟踪的实体,而不适用于数据库:因此我们可以执行如下操作:

modelBuilder.Entity<Airline>(x =>
        {
            x.HasKey(a => a.Id);

            x.HasOne(c => c.Country)
            .WithMany(a => a.Airlines)
            .HasForeignKey(a => a.CountryId)
            .OnDelete(DeleteBehavior.ClientCascade);
            .IsRequired(false);
        });

        modelBuilder.Entity<Airport>(x =>
        {
            x.HasKey(a => a.Id);

            x.HasOne(c => c.Country)
            .WithMany(a => a.Airports)
            .HasForeignKey(a => a.CountryId)
            .OnDelete(DeleteBehavior.ClientCascade);
            .IsRequired(false);
        });

Note: You can apply same for Flight as well.注意:您也可以对Flight应用相同的方法。 In addition, As you may know OnDelete(DeleteBehavior.ClientCascade);此外,您可能知道OnDelete(DeleteBehavior.ClientCascade); or ClientCascade allows the DBContext to delete entities even if there is a cyclic ref or LOCK on it.或者ClientCascade允许DBContext删除实体,即使上面有cyclic refLOCK Please read the official guideline for more details here请在此处阅读官方指南以获取更多详细信息

add these lines in "OnModelCreating"在“OnModelCreating”中添加这些行

 var cascades = modelBuilder.Model.GetEntityTypes()
                    .SelectMany(t => t.GetForeignKeys())
                    .Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade);
    
    
    foreach (var fk in cascades)
         fk.DeleteBehavior = DeleteBehavior.Restrict;

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

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