I am creating code first Entity framework and when I using fluent API to manage the mapping I have the following code
modelBuilder.Entity<Room>()
.HasOne(r => r.Creator)
.WithMany(u => u.Rooms)
.HasForeignKey(p => p.CreatorId)
.IsRequired()
.OnDelete(DeleteBehavior.SetNull);
so my question is there is any conflict in this chaining method as IsRequired() is used to make the foreign key not allowed to be null and the ".OnDelete(DeleteBehavior.SetNull);" set the foreign key to null when u (referred to the user) entity is deleted, so what would be the behavior of this chaining method when the user associated with this room is deleted, is the operation will be done and the foreign will be set to null or the operation will be restricted?
Yes, they will conflict. If you use DeleteBehavior.SetNull, your CreatorId will be set to nullable during the migration. However, if you set CreatorId to required, they will conflict and you will not be able to successfully migrate and successfully created the relationship.
If you need the CreatorId attribute to be required, then it is recommended that you useļ¼
modelBuilder.Entity<Room>()
.HasOne(r => r.Creator)
.WithMany(u => u.Rooms)
.HasForeignKey(p => p.CreatorId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
else,you can use:
modelBuilder.Entity<Room>()
.HasOne(r => r.Creator)
.WithMany(u => u.Rooms)
.HasForeignKey(p => p.CreatorId)
.OnDelete(DeleteBehavior.SetNull);
For more details,you can see this article: The Fluent API OnDelete Method.
Related
I'm using the Entity Framework Core 6 fluent API to configure my database schema in a .NET Core project.
When declaring two-way relationships we can easily specify the foreign key like this:
modelBuilder.Entity<Foo>()
.HasMany(x => x.Bars)
.WithOne(x => x.Foo)
.HasForeignKey(x => x.FooId);
However, if we have a one-way only relationship like this:
modelBuilder.Entity<Foo>()
.HasOne(x => x.Bar);
I don't understand how to specify the foreign key, since the .HasOne method does not return an object that has the .HasForeignKey() method.
How do you specify the foreign key in these cases?
Try to do something like this:
modelBuilder.Entity<Foo>()
.HasOne(x => x.Bar)
.WithOne()
.HasForeignKey(e => e.Whatever);
Also this one maybe can help you too check
According to Enabling Cascade Delete on Microsoft's web site:
You can remove these cascade delete conventions by using:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()
The following code configures the relationship to be required and then disables cascade delete.
C#
modelBuilder.Entity<Course>()
.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID)
.WillCascadeOnDelete(false);
So, as far as I understood, Remove<OneToManyCascadeDeleteConvention>() removes cascade delete for all entities in this context, while WillCascadeOnDelete(false) only removes only the related entity (Course entity in the example above). Is that true?
I have a Booking class that has a booking contact (a Person) and a set of navigation properties (People) that links through a join table to another set of navigation properties (Bookings) in Person. How do I generate the Booking table with cascading deletes enabled for the booking contact relationship? When I leave it out of the fluent API code (default setting of cascade delete enabled) I get the following error message from migration:
Introducing FOREIGN KEY constraint
'FK_dbo.BookingPeople_dbo.People_PersonID' on table 'BookingPeople'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Could not create constraint or index. See previous errors.
modelBuilder.Entity<Person>()
.HasMany<Booking>(s => s.aBookings)
.WithRequired(s => s.Contact)
.HasForeignKey(s => s.ContactId);
modelBuilder.Entity<Booking>()
.HasMany(t => t.People)
.WithMany(t => t.Bookings)
.Map(m => {
m.ToTable("BookingPeople");
m.MapLeftKey("BookingID");
m.MapRightKey("PersonID");
});
The problem is you have multiple paths of cascade deletes that could end trying to delete the same row in the BookingPeople table in DB.
You can avoid such ambiguous delete paths by either disabling cascading delete in the one-to-many relationship using Fluent API:
modelBuilder.Entity<Booking>()
.HasRequired(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId)
.WillCascadeOnDelete(false);
Or by defining the relationship as optional (with a nullable foreign key, but you can not configure the relationship with cascade delete using Fluent Api).
modelBuilder.Entity<Booking>()
.HasOptional(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId);// ContactId is a nullable FK property
Also, you can remove the cascade delete convention by using:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Or in the case of the many-to-many relationship:
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
If you need to delete all the Bookings asociated with a Person when you delete it, my advice is configure the one-to-many relationship as optional, and override the SaveChanges method:
public override int SaveChanges()
{
Bookings.Local
.Where(r => r.ContactId == null)
.ToList()
.ForEach(r => Bookings.Remove(r));
return base.SaveChanges();
}
If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null. This way, you can find the orphans in the SaveChanges method and delete them
I have thought:
HasMany(d => d.Categories)
.WithRequired()
.HasForeignKey(c => c.RelatedEntityId)
.WillCascadeOnDelete(true);
This piece of code mean that, d have required at least one category?
I have a Booking class that has a booking contact (a Person) and a set of navigation properties (People) that links through a join table to another set of navigation properties (Bookings) in Person. How do I generate the Booking table with cascading deletes enabled for the booking contact relationship? When I leave it out of the fluent API code (default setting of cascade delete enabled) I get the following error message from migration:
Introducing FOREIGN KEY constraint
'FK_dbo.BookingPeople_dbo.People_PersonID' on table 'BookingPeople'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Could not create constraint or index. See previous errors.
modelBuilder.Entity<Person>()
.HasMany<Booking>(s => s.aBookings)
.WithRequired(s => s.Contact)
.HasForeignKey(s => s.ContactId);
modelBuilder.Entity<Booking>()
.HasMany(t => t.People)
.WithMany(t => t.Bookings)
.Map(m => {
m.ToTable("BookingPeople");
m.MapLeftKey("BookingID");
m.MapRightKey("PersonID");
});
The problem is you have multiple paths of cascade deletes that could end trying to delete the same row in the BookingPeople table in DB.
You can avoid such ambiguous delete paths by either disabling cascading delete in the one-to-many relationship using Fluent API:
modelBuilder.Entity<Booking>()
.HasRequired(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId)
.WillCascadeOnDelete(false);
Or by defining the relationship as optional (with a nullable foreign key, but you can not configure the relationship with cascade delete using Fluent Api).
modelBuilder.Entity<Booking>()
.HasOptional(s => s.Contact)
.WithMany(s => s.aBookings)
.HasForeignKey(s => s.ContactId);// ContactId is a nullable FK property
Also, you can remove the cascade delete convention by using:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
Or in the case of the many-to-many relationship:
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
If you need to delete all the Bookings asociated with a Person when you delete it, my advice is configure the one-to-many relationship as optional, and override the SaveChanges method:
public override int SaveChanges()
{
Bookings.Local
.Where(r => r.ContactId == null)
.ToList()
.ForEach(r => Bookings.Remove(r));
return base.SaveChanges();
}
If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null. This way, you can find the orphans in the SaveChanges method and delete them