Cascade deleting multiple tables - entity-framework

I have 4 tables. Cities, Providers, ProviderAdmins, AspNetUsers.
I want to setup following behaviour:
If i delete Cities entry - City was deleted.
If i delete AspNetUsers entry - User was deleted -> ProviderAdmin was
deleted
If i delete ProviderAdmins entry - ProviderAdmin was deleted -> User was deleted
If i delete Provider entry - Provider was deleted -> ProviderAdmin
was deleted -> User was deleted.
Now i even can't create my db from my model, because getting following issue:
Introducing FOREIGN KEY constraint 'FK_ProviderAdmins_Providers_ProviderId' on table 'ProviderAdmins' 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.
My entities:
public class City
{
public long Id { get; set; }
public string Region { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public virtual ICollection<ProviderAdmin> ProviderAdmins { get; set; }
}
public class User : IdentityUser
{
public string LastName { get; set; }
public string MiddleName { get; set; }
public string FirstName { get; set; }
public virtual ProviderAdmin ProviderAdmin { get; set; }
}
public class Provider
{
public long Id { get; set; }
public virtual ICollection<ProviderAdmin> ProviderAdmins { get; set; }
}
public class ProviderAdmin
{
public long Id { get; set; }
[Required]
[ForeignKey("UserId")]
public string UserId { get; set; }
public virtual User User { get; set; }
[Required]
public long ProviderId { get; set; }
public virtual Provider Provider { get; set; }
public long? CityId { get; set; }
public virtual City City { get; set; }
}
Fluent Api:
builder.Entity<Provider>()
.HasMany(pa => pa.ProviderAdmins)
.WithOne(p => p.Provider)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<City>()
.HasMany(pa => pa.ProviderAdmins)
.WithOne(p => p.City)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<User>()
.HasOne(u => u.ProviderAdmin)
.WithOne(p => p.User)
.HasForeignKey<ProviderAdmin>(p => p.UserId)
.OnDelete(DeleteBehavior.Cascade);
Please, give me advice about how can i fix error mentioned above, and how can i setup behaviour which i want to get.
Thank you so much for your attention.
UPD: I made relationship PAs - AspNetUsers to be one or zero to one. It is still not working, i still missing something. I know that problem is in relationship between provideradmin and users but i really don't know why it not allowed.

Related

EF Core may cause cycles or multiple cascade paths failure

I'm trying to set the Model builder cascade property but I'm not getting the syntax correct.
Simple database:
I have a class (ReqForBetaReader):
public class ReqForBetaReader
{
public ReqForBetaReader()
{
Tags = new List<TagForReqBeta>();
}
public Guid Id { get; set; }
public string Title { get; set; }
public string Synopsis { get; set; }
public int WordCount { get; set; }
public Guid AuthorId { get; set; }
public virtual User Author { get; set; }
public DateTime UpdatedOn { get; set; }
public virtual ICollection<TagForReqBeta> Tags { get; set; }
}
I have my TagForReqBeta class
public class TagForReqBeta
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Guid ReqForBetaReaderId { get; set; }
public virtual ReqForBetaReader ReqForBetaReader { get; set; }
[Required]
public Guid UserId { get; set; }
public virtual User User { get; set; }
public DateTime AddedOn { get; set; }
}
So a ReqForBeta can have multiple tags.
When I ran Update-Database, I got the dreaded:
Introducing FOREIGN KEY constraint 'FK_TagsOnRequestForBetaReaders_RequestsForBetaReaders_ReqForBetaReaderId' on table 'TagsOnRequestForBetaReaders' 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.
I believe this means that when I delete a ReqForBeta, I should also delete all the TagForReqBeta as well, but I'm having trouble with the syntax. Any help?
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
Since ReqForBetaReaderId is not nullable, when you delete ReqForBetaReader record you will have to delete TagForReqBeta.
Since as I understand the same tag can be used for several records you have to make ReqForBetaReaderId nullable
[Required]
public Guid? ReqForBetaReaderId { get; set; }
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.ClientSetNull);
and as #IvanStoev noticed you have the same problem with User

Introducing FOREIGN KEY constraint on table 'ImageTag' may cause cycles or multiple cascade paths

In Entity Framework Core, I'm attempting to create a system with 4 Db Models - Image, Tag, ImageTag, User. Image and Tag are a many to many relationship, which is provided by the ImageTag table. Images and Tags are also directly linked to Users in a many to one relationship (Images are the property of Users, so it's a required relationship), so when a User is deleted it should cascade and delete all Images, Tags & ImageTags belonging to that User. When I try to implement this and do an Update-Database with the code below I get an error of:
Introducing FOREIGN KEY constraint 'FK_ImageTag_Tags_TagId' on table 'ImageTag' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
After much trial and error, it seems one solution to the problem is to make UserId on Image and Tag nullable. This changes the ReferentialAction in the constraints for the foreign key on those tables from ReferentialAction.Cascade to ReferentialAction.Restrict. However...
1) I'm not entirely sure why this fixes the issue as the error refers to the ImageTag constraint, not the User one.
2) I don't want to make the relationship between the the Images/Tags and Users an optional/nullable one.
3) However this does get solved, ideally it would still allow cascading deletes where appropriate.
public class Image
{
public long Id { get; set; }
public string FileExtension { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<ImageTag> ImageTags { get; set; }
}
public class Tag
{
public long Id { get; set; }
public string Name { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<ImageTag> ImageTags { get; set; }
}
public class ImageTag
{
public long ImageId { get; set; }
public long TagId { get; set; }
public virtual Image Image { get; set; }
public virtual Tag Tag { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection<Image> Images { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
public class MyDbContext : DbContext
{
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ImageTag>()
.HasKey(x => new { x.ImageId, x.TagId });
modelBuilder.Entity<ImageTag>()
.HasOne(bc => bc.Image)
.WithMany(b => b.ImageTags)
.HasForeignKey(bc => bc.ImageId);
modelBuilder.Entity<ImageTag>()
.HasOne(bc => bc.Tag)
.WithMany(c => c.ImageTags)
.HasForeignKey(bc => bc.TagId);
}
}

Code First: FOREIGN KEY constraint may cause cycles or multiple cascade paths

I'm working on an application related to the education industry, and I have the following set of classes.
public class Person : EntityBase<long>
{
public string Surname { get; set; }
public string First { get; set; }
public string Middle { get; set; }
public Gender Gender { get; set; }
public DateTime Dob { get; set; }
}
public class Student: Person
{
public string AdmissionId { get; set; }
public string StudentId { get; set; }
public List<GuardianLink> Guardians { get; set; }
public StudentStatus Status { get; set; }
}
public class Guardian: Person
{
public GuardianType GuardianType { get; set; }
public List<GuardianLink> Students { get; set; }
}
public class GuardianLink: EntityBase<long>
{
[Required]
public Student Student { get; set; }
[Required]
public Guardian Guardian { get; set; }
public Boolean IsEmergencyContact { get; set; }
public Boolean IsAllowedToPickup { get; set; }
}
When I tried to create the database from these classes, I received an error that this would result in circular cascading deletes. So I added the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Guardian)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Student)
.WithMany()
.WillCascadeOnDelete(false);
}
I expected this to resolve the issue, but I'm still getting an error.
Introducing FOREIGN KEY constraint
'FK_dbo.GuardianLinks_dbo.People_Student_Id' on table 'GuardianLinks'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
I want to configure this so that deleting a student deletes the associated links, but not the guardian, and vice versa when deleting a guardian. Why didn't this resolve the issue? What should I be doing instead?

EF Many to Many cascading delete

I have an issue.
I have some classes that look like this:
public class Question
{
public int Id { get; set; }
[Required] [MaxLength(255)] public string Text { get; set; }
public int CriteriaId { get; set; }
public int QuestionGroupId { get; set; }
public QuestionGroup QuestionGroup { get; set; }
public virtual IList<Answer> Answers { get; set; }
public virtual Criteria Criteria { get; set; }
}
public class Answer
{
public int Id { get; set; }
[Required] [MaxLength(255)] public string Text { get; set; }
public int QuestionId { get; set; }
public Question Question { get; set; }
public virtual IList<State> States { get; set; }
}
public class Criteria
{
public int Id { get; set; }
[Required] [MaxLength(100)] public string Name { get; set; }
public virtual IList<State> States { get; set; }
public IList<Question> Questions { get; set; }
}
public class State
{
public int Id { get; set; }
public int CriteriaId { get; set; }
[Required] [MaxLength(100)] public string Name { get; set; }
public int Score { get; set; }
public virtual IList<Filter> Filters { get; set; }
public Criteria Criteria { get; set; }
}
They have a many to many relationship, so I created this mapping:
modelBuilder.Entity<Answer>()
.HasMany(m => m.States)
.WithMany()
.Map(m => {
m.MapLeftKey("AnswerId");
m.MapRightKey("StateId");
m.ToTable("AnswerStates");
});
When I tried to update my database I get an error about foreign keys.
So I added this line (as a temporary fix):
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
But I need the cascading delete.
I can't seem to figure out why it won't let me have it.
The error I get when trying to include cascading deletes is:
Introducing FOREIGN KEY constraint 'FK_dbo.AnswerStates_dbo.States_StateId' on table 'AnswerStates' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I hope I have provided enough information. Does anyone know what I can do to allow the cascading delete (I need to be able to delete a State or an Answer and it to remove the record from AnswerStates
This is pretty much the same as another question (Deleting only one entry from Many-to-Many relationship) I was answering today. Just your case is a bit more complicated.
The multiple cascade path in your case is from Criteria to AnswerStates. When deleting a Criteria record, the AnswerStates records can be deleted either by Criteria->States->AnswerStates or Criteria->Questions->Answers->AnswerStates relationships.
The solution is always one and the same - turn one of the relationships cascade delete off and handle deletion either manually or via trigger.
In this particular case, my suggestion is to turn Criteria->States cascade delete off:
modelBuilder.Entity<Criteria>()
.HasMany(m => m.States)
.WithRequired(d => d.Criteria)
.HasForeignKey(d => d.CriteriaId)
.WillCascadeOnDelete(false);
and use something like this before deleting a Criteria:
db.States.RemoveRange(db.States.Where(s => s.CriteriaId == criteriaId_to_be_removed));

Entity Framework 4.3.1 how to create associations

my code like below
public class User
{
public int ID { get; set; }
public int BillingAddressID { get; set; }
public Address BillingAddress { get; set; }
public IList<Shipment> Shipments { get; set; }
}
public class Address
{
public int ID { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Shipment
{
public int ID { get; set; }
public string State { get; set; }
public int DeliveryAddressID { get; set; }
public Address DeliveryAddress { get; set; }
public User ShipUser { get; set; }
//[ForeignKey("ShipUser")]
public int ShipUserID { get; set; }
//public int UserId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Shipment> Shipments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Shipment>().HasRequired(u => u.ShipUser)
.WithMany(d => d.Shipments)
.HasForeignKey(c => c.ShipUserID)
.WillCascadeOnDelete(false);
}
}
if i remove the override method,i will get an error "SqlException: Introducing FOREIGN KEY constraint 'FK_Shipments_Users_ShipUserID' on table 'Shipments' 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. See previous errors."
if i remove ShipUserID in Shipment Class,it will work ok,when i see the table that is created by ef,i found a column named Shipment_UserID in table Shipment.I don`t know why.
if rename the class indenty key to UserID,it also work ok.
I try it anyway,but I don`t know the reason, I need some books about EF associations.
If you don't have mapping specified without cascadeDelete=false for one relationship it will create multiple cascade paths if you have tow relationships to user from Shipment.
By convention you can use public
Public User ShipUser { get; set; }
public int ShipUserID { get; set; }
it will use ShipUserID as foreign key by convention.
If you remove ShipUserID Ef need to create his own foreign key to keep the relationship . that is your ' Shipment_UserID'
rename the class indenty key to UserID I don't understand what you meant.
Here is a good tutorial to start with