[英]Entity Framework Core Code-First: Cascade delete on a many-to-many relationship
I'm working on an ASP.NET MVC 6 project with Entity-Framework Core (version "EntityFramework.Core": "7.0.0-rc1-final"
) backed by a SQL Server 2012 express DB. 我正在使用SQL Server 2012 Express DB支持的Entity-Framework Core(版本
"EntityFramework.Core": "7.0.0-rc1-final"
)的ASP.NET MVC 6项目。
I need to model a many-to-many relationship between a Person
entity and an Address
entity. 我需要模拟
Person
实体和Address
实体之间的多对多关系。 As per this guide I modeled it with a PersonAddress
join-table entity, because this way I can store some extra info. 根据本指南,我使用
PersonAddress
连接表实体对其进行建模,因为这样我可以存储一些额外的信息。
My goal is to set-up my system this way: 我的目标是以这种方式设置我的系统:
Person
instance is deleted, all the related PersonAddress
instances must be deleted. Person
实例,则必须删除所有相关的PersonAddress
实例。 All the Address
instances they reference to must be deleted too, only if they are not related to other PersonAddress
instances. PersonAddress
实例无关时,它们引用的所有Address
实例也必须被删除。 PersonAddress
instance is deleted, the Address
instance it relates to must be deleted only if it is not related to other PersonAddress
instances. PersonAddress
实例被删除,该Address
涉及实例必须只有当它不与其他删除PersonAddress
实例。 All Person
instances must live. Person
实例都必须存在。 Address
instance is deleted, all the related PersonAddress
instances must be deleted. Address
实例,则必须删除所有相关的PersonAddress
实例。 All Person
instances must live. Person
实例都必须存在。 I think most of the work must be done in the many-to-many relationship between Person
and Address
, but I expect to write some logic too. 我认为大多数工作必须在
Person
和Address
之间的多对多关系中完成,但我希望也写一些逻辑。 I will leave this part out of this question. 我将把这一部分从这个问题中解脱出来。 What I'm interested in is how to configure my many-to-many relationship.
我感兴趣的是如何配置我的多对多关系。
Here is the current situation . 这是目前的情况 。
This is the Person
entity. 这是
Person
实体。 Please note that this entity has got one-to-many relationships with other secondary entities. 请注意,此实体与其他辅助实体具有一对多关系。
public class Person
{
public int Id {get; set; } //PK
public virtual ICollection<Telephone> Telephones { get; set; } //navigation property
public virtual ICollection<PersonAddress> Addresses { get; set; } //navigation property for the many-to-many relationship
}
This is the Address
entity. 这是
Address
实体。
public class Address
{
public int Id { get; set; } //PK
public int CityId { get; set; } //FK
public City City { get; set; } //navigation property
public virtual ICollection<PersonAddress> People { get; set; } //navigation property
}
This is the PersonAddress
entity. 这是
PersonAddress
实体。
public class PersonAddress
{
//PK: PersonId + AddressId
public int PersonId { get; set; } //FK
public Person Person {get; set; } //navigation property
public int AddressId { get; set; } //FK
public Address Address {get; set; } //navigation property
//other info removed for simplicity
}
This is the DatabaseContext
entity, where all the relationships are described. 这是
DatabaseContext
实体,其中描述了所有关系。
public class DataBaseContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
//All the telephones must be deleteded alongside a Person.
//Deleting a telephone must not delete the person it refers to.
builder.Entity<Person>()
.HasMany(p => p.Telephones)
.WithOne(p => p.Person);
//I don't want to delete the City when I delete an Address
builder.Entity<Address>()
.HasOne(p => p.City)
.WithMany(p => p.Addresses)
.IsRequired().OnDelete(Microsoft.Data.Entity.Metadata.DeleteBehavior.Restrict);
//PK for the join entity
builder.Entity<PersonAddress>()
.HasKey(x => new { x.AddressId, x.PersonId });
builder.Entity<PersonAddress>()
.HasOne(p => p.Person)
.WithMany(p => p.Addresses)
.IsRequired();
builder.Entity<PersonAddress>()
.HasOne(p => p.Address)
.WithMany(p => p.People)
.IsRequired();
}
}
Both Telephone
and City
entities have been removed for the sake of simplicity. 为简单起见,
Telephone
和City
实体都已删除。
This is the code for removing a Person
. 这是删除
Person
的代码。
Person person = await _context.People.SingleAsync(m => m.Id == id);
try
{
_context.People.Remove(person);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
}
As for my readings avoiding .Include()
will let the DB take care of the eventual CASCADE
deletes. 至于我的读数,避免
.Include()
将让DB处理最终的CASCADE
删除。 I'm sorry but I don't remember the SO question where this concept was clarified. 对不起,但我不记得澄清这个概念的SO问题。
If I run this code I can seed the DB using this workaround . 如果我运行此代码,我可以使用此解决方法为数据库播种。 When I want to test-deleting a
Person
entity with the above code, I get this exception: 当我想使用上面的代码测试删除
Person
实体时,我得到以下异常:
The DELETE statement conflicted with the REFERENCE constraint "FK_PersonAddress_Person_PersonId". The conflict occurred in database "<dbName>", table "<dbo>.PersonAddress", column 'PersonId'.
The statement has been terminated.
I tested several relationship setups in the DatabaseContext.OnModelCreating
method without any luck. 我在
DatabaseContext.OnModelCreating
方法中测试了几个关系设置而没有任何运气。
Finally, here's my question . 最后,这是我的问题 。 How should I configure my many-to-many relationship in order to correctly delete a
Person
and its related entities from my application, according to the goal described before? 根据之前描述的目标 ,我应该如何配置我的多对多关系以便从我的应用程序中正确删除
Person
及其相关实体?
Thank you all. 谢谢你们。
First I see you have set City and Address relationship with DeleteBehavior.Restrict
and you say: ' //I don't want to delete the City when I delete an Address '. 首先,我看到你已经设置了与
DeleteBehavior.Restrict
城市和地址关系,你说:' //我删除地址时不想删除城市 '。
But you don't need Restrict here, because even with DeleteBehavior.Cascade
City will not be deleted. 但是你不需要在这里限制,因为即使使用
DeleteBehavior.Cascade
City也不会被删除。 You are looking it from the wrong side. 你是从错误的一面看。 What
Cascade
here does is when a City is deleted all addresses belonging to it are also deleted. Cascade
在这里做的是删除城市时,所有属于它的地址也会被删除。 And that behavour is logical. 而这种行为是合乎逻辑的。
Secondly your many-to-many relationship is fine. 其次你的多对多关系很好。 When deleting Person its links from PersonAddress Table will automatically be deleted because of Cascade.
删除Person时,由于Cascade,将自动删除PersonAddress Table中的链接。 And if you want also to delete Addresses that were connected only to that Person you will have to do it manually.
如果您还想删除仅与该Person连接的地址,则必须手动执行此操作。 You actually have to delete those Addresses before deleting Person is order to know what to delete.
在删除Person之前,您实际上必须删除这些地址,以便知道要删除的内容。
So logic should be following: 所以逻辑应遵循:
1. Query through all record of PersonAddress where PersonId = person.Id
; 1.查询PersonAddress的所有记录,其中
PersonId = person.Id
;
2. Of those take only ones that have single occurance of AddressId in PersonAddress table, and delete them from Person table. 2.那些只接受在PersonAddress表中单独出现AddressId的那些,并从Person表中删除它们。
3. Now delete the Person. 3.现在删除Person。
You could do this in code directly, or if you want database to do it for you, trigger could be created for step 2 with function: When row from PersonAddress is about to be deleted check if there are no more rows with same AddressId in that PersonAddress table in which case delete it from Address table. 您可以直接在代码中执行此操作,或者如果您希望数据库为您执行此操作,可以使用函数为步骤2创建触发器:当要删除PersonAddress中的行时,检查是否没有更多具有相同AddressId的行PersonAddress表,在这种情况下从Address表中删除它。
More info here: 更多信息:
How to cascade delete over many to many table 如何级联删除多对多表
How do I delete from multiple tables using INNER JOIN in SQL server 如何在SQL Server中使用INNER JOIN从多个表中删除
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.