EF Core 中多对多关系的级联参照完整性约束
Cascading Referential Integrity Constraints for many to many relationships in EF Core
我将 EF Core 3.1.5 与 Asp.Net Core 3.1 一起使用。当我尝试添加迁移时,出现下一个错误:
Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_PhotoDevice_Device_DeviceId' on table 'PhotoDevice' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
我有下一个实体和配置:
public class User {
[Key]
public long Id { get; set; }
// Fields
public IEnumerable<Device> Devices { get; set; }
public IEnumerable<Photo> Photos { get; set; }
}
public class Photo {
[Key]
public long Id { get; set; }
// Fields
[ForeignKey("User")]
public long UserRef { get; set; }
public User User { get; set; }
public IEnumerable<PhotoDevice> PhotoDevices { get; set; }
}
public class Device {
[Key]
public long Id { get; set; }
// Fields
[ForeignKey("User")]
public long UserRef { get; set; }
public User User { get; set; }
public IEnumerable<PhotoDevice> PhotoDevices { get; set; }
}
public class PhotoDevice {
{
public long? PhotoRef { get; set; }
public Photo Photo { get; set; }
public long? DeviceRef { get; set; }
public Device Device { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Device> Devices { get; set; }
public DbSet<PhotoDevice> PhotoDevices { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
Database.Migrate();
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Photo>()
.HasOne(photo => photo.User)
.WithMany(user => user.Photos);
builder.Entity<Device>()
.HasOne(device => device.User)
.WithMany(user => user.Devices);
builder.Entity<PhotoDevice>()
.HasKey(bc => new { bc.PhotoRef, bc.DeviceRef });
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Photo)
.WithMany(b => b.PhotoDevices)
.HasForeignKey(bc => bc.PhotoRef);
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Device)
.WithMany(c => c.PhotoDevices)
.HasForeignKey(bc => bc.DeviceRef);
}
}
我发现问题与级联连接的不唯一性有关。但现在我很困惑。因为我认为这个结构是必要的,我不想放弃任何 FK。如果我需要下一个逻辑,我该如何解决问题:
- 当用户删除所有与用户相关的设备和照片时
- 当删除设备或照片时,所有与删除实体相关的 PhotoDevice 记录也会被删除
您不需要删除任何外键。只需使用 OnDelete(DeleteBehavior)
明确指定您需要哪种级联行为。
例如,以下将导致您的模型创建成功,但对于您的实际应用程序,您需要自己决定在何处以及如何打破级联:
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Photo)
.WithMany(b => b.PhotoDevices)
.HasForeignKey(bc => bc.PhotoRef)
.OnDelete(DeleteBehavior.Restrict); // <-- define cascading behavior
有关详细信息,请参阅 Relationships: Cascade delete and Cascade Delete。
这不是EF Core,而是SQL服务器限制(因此SQL服务器也会抛出异常)
这里有进一步的资源,处理这个限制并展示如何解决它,通过使用INSTEAD OF
触发器,如果打破级联不是一个选项你可以忍受:
lauxjpn说的是对的,但是请注意,添加的时候.OnDelete(DeleteBehavior.Restrict);
之后到你的context,请记得删除你之前的所有migrations,不然还是会报错。
我将 EF Core 3.1.5 与 Asp.Net Core 3.1 一起使用。当我尝试添加迁移时,出现下一个错误:
Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_PhotoDevice_Device_DeviceId' on table 'PhotoDevice' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
我有下一个实体和配置:
public class User {
[Key]
public long Id { get; set; }
// Fields
public IEnumerable<Device> Devices { get; set; }
public IEnumerable<Photo> Photos { get; set; }
}
public class Photo {
[Key]
public long Id { get; set; }
// Fields
[ForeignKey("User")]
public long UserRef { get; set; }
public User User { get; set; }
public IEnumerable<PhotoDevice> PhotoDevices { get; set; }
}
public class Device {
[Key]
public long Id { get; set; }
// Fields
[ForeignKey("User")]
public long UserRef { get; set; }
public User User { get; set; }
public IEnumerable<PhotoDevice> PhotoDevices { get; set; }
}
public class PhotoDevice {
{
public long? PhotoRef { get; set; }
public Photo Photo { get; set; }
public long? DeviceRef { get; set; }
public Device Device { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Device> Devices { get; set; }
public DbSet<PhotoDevice> PhotoDevices { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
Database.Migrate();
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Photo>()
.HasOne(photo => photo.User)
.WithMany(user => user.Photos);
builder.Entity<Device>()
.HasOne(device => device.User)
.WithMany(user => user.Devices);
builder.Entity<PhotoDevice>()
.HasKey(bc => new { bc.PhotoRef, bc.DeviceRef });
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Photo)
.WithMany(b => b.PhotoDevices)
.HasForeignKey(bc => bc.PhotoRef);
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Device)
.WithMany(c => c.PhotoDevices)
.HasForeignKey(bc => bc.DeviceRef);
}
}
我发现问题与级联连接的不唯一性有关。但现在我很困惑。因为我认为这个结构是必要的,我不想放弃任何 FK。如果我需要下一个逻辑,我该如何解决问题:
- 当用户删除所有与用户相关的设备和照片时
- 当删除设备或照片时,所有与删除实体相关的 PhotoDevice 记录也会被删除
您不需要删除任何外键。只需使用 OnDelete(DeleteBehavior)
明确指定您需要哪种级联行为。
例如,以下将导致您的模型创建成功,但对于您的实际应用程序,您需要自己决定在何处以及如何打破级联:
builder.Entity<PhotoDevice>()
.HasOne(bc => bc.Photo)
.WithMany(b => b.PhotoDevices)
.HasForeignKey(bc => bc.PhotoRef)
.OnDelete(DeleteBehavior.Restrict); // <-- define cascading behavior
有关详细信息,请参阅 Relationships: Cascade delete and Cascade Delete。
这不是EF Core,而是SQL服务器限制(因此SQL服务器也会抛出异常)
这里有进一步的资源,处理这个限制并展示如何解决它,通过使用INSTEAD OF
触发器,如果打破级联不是一个选项你可以忍受:
lauxjpn说的是对的,但是请注意,添加的时候.OnDelete(DeleteBehavior.Restrict);
之后到你的context,请记得删除你之前的所有migrations,不然还是会报错。