Entity Framework relationship between entities, cycles or multiple cascade paths - entity-framework

I am newbie with EF and trying to design my database using Code First. Following is my three Entities (I have not written all properties). I have an ode situation. The administrator is not allowed to edit an Expense, so I had to create another entity (EditedExpense) to have all edited expenses in another table. So when Administrator tries to change (edit) an expense and clicks save, a new post in EditedExpense will be created.
public class Expense
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ExpenseId { get; set; }
[Required]
public int CategoryId{ get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
public virtual List<EditedExpense> EditedExpenses { get; set; }
}
public class EditedExpense
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int EditedExpenseId { get; set; }
[Required]
public int CategoryId{ get; set; }
[ForeignKey("CategoryId")]
public virtual Category Category { get; set; }
public int ExpenseId { get; set; }
}
public class Category
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int CategoryId{ get; set; }
public string Title
public virtual List<Expense> Expenses { get; set; }
public virtual List<EditedExpense> EditedExpenses { get; set; }
}
It generates this error Introducing FOREIGN KEY constraint 'Expense_EditedExpenses' on table 'EditedExpense' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints

You need to turn off the cascade delete, either on Category or EditedExpenses on Expense entity by doing this.
public class YourDbContext : DbContext
{
// other code ommitted
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Remove cascade delete from Category.
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Category)
.WithMany(c => c.Expenses)
.WillCascadeOnDelete(false);
// Or remove cascade delete from Edited Expense.
//modelBuilder.Entity<Expense>()
// .HasMany(e => e.EditedExpenses)
// .WithRequired()
// .WillCascadeOnDelete(false);
}
}
Here is the explanation why multiple cascade is not allowed.

Related

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

fluent API on table that has relation with itself

I've created a table that has a relation with itself the table has a one-to-many relationship here is my Entity:
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentId { get; set; }
#region Relations
[ForeignKey("ParentId")]
public virtual ICollection<Permission> Permissions { get; set; }
#endregion
}
but when I used migration to create the table in SQL, update-database failed for this error:
Introducing FOREIGN KEY constraint 'FK_Permission_Permission_ParentId' on table 'Permission' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
So I decided to use fluent API to solve this issue but I don't know how to Specify ON DELETE NO ACTION by Fluent API on a table that has a relation with itself. any help?
is there any solution to solve my problem?
For EF Core the Entity should normally have two Navigation Properties, like this:
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentPermissionId { get; set; }
#region Relationships
public virtual Permission ParentPermission { get; set; }
public virtual ICollection<Permission> ChildPermissions { get; } = new HashSet<Permission>();
#endregion
}
And configured like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Permission>()
.HasMany(p => p.ChildPermissions)
.WithOne(p => p.ParentPermission)
.HasForeignKey(p => p.ParentPermissionId)
.OnDelete(DeleteBehavior.Restrict);
base.OnModelCreating(modelBuilder);
}

Cyclic or multiple cascade paths error with EF CodeFirst

I develop an asp.net mvc solution with Entity Framework Code First and I got the error:
Introducing FOREIGN KEY constraint 'FK_dbo.Transports_dbo.Shippers_ReceiverId' on table 'Transports' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
This occurred when starting the solution in the process of creation of the database.
Here are my models:
public class Transport
{
[Key]
public int Id { get; set; }
...
public int SenderId { get; set; }
public int ReceiverId { get; set; }
...
public virtual Shipper Sender { get; set; }
public virtual Shipper Receiver { get; set; }
}
public class Shipper
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string Number { get; set; }
}
If I comment the public virtual Shipper Receiver { get; set; } then it works so this is the cyclic problem.
Does anyone can help me on this problem?
For more complex 'self-bound' or 'multiple to same' relationships, you need to explicitly define the relationships - best using fluent configuration.
e.g.
modelBuilder.Entity<Transport>()
.HasRequired(at => at.Sender)
.WithMany(a => a.TransportsAsSender)
// .HasForeignKey(at => at.SenderId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Transport>()
.HasRequired(at => at.Receiver)
.WithMany(a => a.TransportsAsReceiver)
// .HasForeignKey(at => at.ReceiverId)
.WillCascadeOnDelete(false);
...that assumes you also add:
public ICollection<Transport> TransportsAsSender { get; set; }
public ICollection<Transport> TransportsAsReceiver { get; set; }
...to the Shipper
Or just use...
.WithMany() // and no collection navigation properties

Defining foreign key constraints with Entity Framework code-first

I have following entity class called Code. It stores categories of different kinds - the data for which I would have otherwise needed to create many small tables e.g. User Categories, Expense Categories, Address types, User Types, file formats etc.
public class Code
{
public int Id { get; set; }
public string CodeType { get; set; }
public string CodeDescription { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
:
: // many more
}
The class Expense looks like this:
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public double Amount { get; set; }
public DateTime ExpenseDate { get; set; }
}
With the above class definitions, I have established 1:many relation between Code and Expense using the CategoryId mapping.
My problem is, I want to map the SourceId field in Expense to the Code object. Which means, Expense object would contain
public Code Source { get; set; }
If I use this, at runtime I get an error about cyclic dependencies.
Can someone please help?
You will need to disable cascading delete on at least one of the two relationships (or both). EF enables cascading delete by convention for both relationships because both are required since the foreign key properties are not nullable. But SQL Server doesn't accept multiple cascading delete paths onto the same table that are introduced by the two relationships. That's the reason for your exception.
You must override the convention with Fluent API:
public class Code
{
public int Id { get; set; }
//...
public virtual ICollection<Expense> Expenses { get; set; }
//...
}
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public virtual Code Source { get; set; }
//...
}
Mapping with Fluent API;
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Category)
.WithMany(c => c.Expenses)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Source)
.WithMany()
.HasForeignKey(e => e.SourceId)
.WillCascadeOnDelete(false);

Foreign Keys in Entity Framework - Cycles or multiple cascade paths error

Using EF Code First i have the following, for example:
public class Blog
{
public int BlogID { get; set; }
public string Content { get; set; }
public virtual User User { get; set; }
public virtual ICollection<BlogMeta> BlogMeta { get; set; }
}
public class BlogMeta
{
public int BlogMetaID { get; set; }
public string Content { get; set; }
public virtual User User { get; set; }
public virtual Blog Blog { get; set; }
}
This successfully generates the tables Blog and BlogMeta and creates the foreign key relationship with the User table. After reading this i changed this to the following:
public class Blog
{
public int BlogID { get; set; }
public string Content { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual ICollection<BlogMeta> BlogMeta { get; set; }
}
public class BlogMeta
{
public int BlogMetaID { get; set; }
public string Content { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public int BlogID { get; set; }
public virtual Blog Blog { get; set; }
}
and now it doesn't work. It generates the tables and then throws the following error when trying to create the relationships:
Introducing FOREIGN KEY constraint 'BlogMeta_User' on table 'BlogMeta' may cause cycles or multiple cascade paths.
So what is the advantage of introducing the public int UserID and why does it fail when doing so?
EDIT:
Ok, so i've come across this answer which outlines the difference between Independent Associations and Foreign Key Associations... which it turns out is what i was talking about. So this leaves the question, why does it throw the above error when using foreign key associations?
As Ladislav mentioned, you are defining multiple cascade paths for BlogMeta entity. You'd have to disable cascade for one of your relationships.
You can add the following method to your context class, to diable cascade for your User-BlogMeta relationship:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogMeta>().HasRequired(bm => bm.User).WithMany().WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
You can indicate the other end of relationship (WithMany(u => u.BlogMetas)) if you have defined a colletion of BlogMeta in your User class.