EF Core may cause cycles or multiple cascade paths failure - entity-framework

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

Related

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 6 cycles or multiple cascade paths

I'm trying to create my database from my models, but I keep getting the error Introducing FOREIGN KEY constraint 'FK_dbo.Reports_dbo.UserProfiles_UserId' on table 'Reports' 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.
anyone knows what might be wrong with my models/setup?
These are all used Models
public class Report {
[Key]
public int Id { get; set; }
[Required]
public string Number { get; set; }
public bool Synced { get; set; }
public DateTime CreationDate { get; set; }
public int NewCommentId { get; set; }
public virtual Comment NewComment { get; set; }
public int UserId { get; set; }
public virtual UserProfile User { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Photo> PhotosBefore { get; set; }
public virtual ICollection<Photo> PhotosAfter { get; set; }
}
public class Photo {
[Key]
public int Id { get; set; }
public string Image { get; set; }
public bool Synced { get; set; }
public DateTime CreationDate { get; set; }
public int ReportId { get; set; }
public virtual Report Report { get; set; }
public int UserId { get; set; }
public virtual UserProfile User { get; set; }
}
public class Comment {
[Key]
public int Id { get; set; }
public DateTime CreationDate { get; set; }
public string Text { get; set; }
public int ReportId { get; set; }
public virtual Report Report { get; set; }
public int UserId { get; set; }
public virtual UserProfile User { get; set; }
}
public class UserProfile {
[Key]
public int Id { get; set; }
public string Stamnummer { get; set; }
public string Leverancier { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Report> Reports { get; set; }
public virtual ICollection<Photo> Photos { get; set; }
}
In order to be certain, we need to see how you have configured your relationships using the model builder in the OnModelCreating method. Based on the error message you have provided, it appears that you have relationships configured so that one of your entities is configured for cascade on delete from two or more other entities.
As an example (this may not be the case, but rather just a means of describing the problem):
User has a one-to-many relationship with Comments
User has a one-to-many relationship with Reports
Report has a one-to-many relationship with Comments
Comment is configured so that a User is required
Comment is configured so that a Report is required
Report is configured so that a User is required
Any one-to-many relationship where the entity one side of the relationship is required is going to have cascade on delete configured by default. In this scenario, if a User were deleted it would trigger a cascade to both Reports and Comments. Each Report would also cause a cascade on Comments.
The solution is to disable cascading deletes for one of the relationships. You can find a similar question to yours here describing what I mentioned above.

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));

Why am I getting an extra foreign key column with Entity Framework Code First Foreign Key Attributes?

I recently came across this strange problem with Entity Framework Code First.
My class looks like this
public class Status
{
[Key]
public int StatusID { get; set; }
public string Name { get; set; }
public int MemberID { get; set; }
[ForeignKey("MemberID")]
public virtual Member Member { get; set; }
public int PosterID { get; set; }
[ForeignKey("PosterID")]
public virtual Member Poster { get; set; }
public virtual ICollection<StatusLike> StatusLikes { get; set; }
public virtual ICollection<StatusComment> StatusComments { get; set; }
}
My Member class looks like this
public class Member
{
[Key]
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Bio { get; set; }
public virtual ICollection<MemberCourseTaken> MemberCourseTakens { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Club> FoundedClubs { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public int AccountSourceID { get; set; }
public AccountSource AccountSource { get; set; }
public int AddressID { get; set; }
public Address Address { get; set; }
public string ProfilePhoto { get; set; }
public int MemberRankID { get; set; }
public MemberRank MemberRank { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
And for whatever reason the database table that is created has the following columns
StatusID
Name
MemberID
PosterID
Member_MemberID
with MemberID, PosterID, and Member_MemberID being foreign keys.
How can I keep Member_MemberID from being generated?
Your Member_MemberID column is created because of the Member.Statuses property. I can imagine that this is not what you want. Probably members and statuses should exist independent of each other, so you need a junction table.
I don't know if you already use the OnModelCreating override of the DbContext, but that's the place to change the mapping between Member and Status:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Member>().HasMany(m => m.Statuses).WithMany();
}
This will create a table MemberStatuses table with the two Id columns as foreign keys. This is a way to model a many-to-many relationship without a navigation property on the "other" side of the association. (I don't think you want a Members property in Status).
I've seen this before. In my case (Using EF 6.1), it was because my Fluent API Mapping was set up like so:
// In my EntityTypeConfiguration<Status>
HasRequired(x => x.Member).WithMany().HasForeignKey(x => x.MemberID);
That code works perfectly fine, but it doesn't tell EF that my Member class's Collection Navigational Property Status ha been taken into account. So, while I explicitly handled the existence of a Member Navigational Property in my Status Class, I now left an orphaned related collection property. That orphaned property, being a collection, tells EF that my Status class needs to have a Foreign Key to it. So it creates that on the Status Class.
To fix it, I had to be 100% explicit.
HasRequired(x => x.Member).WithMany(x => x.Statuses).HasForeignKey(x => x.MemberID)
It could bee that your Statuses Collection property in Member needs an attribute telling it that it is already considered, and not to go auto-creating mappings. I don't know that attribute.

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