Cyclic or multiple cascade paths error with EF CodeFirst - entity-framework

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

Related

Defining multiple Foreign Key for the Same table in ASP.NET MVC Core Entity Framework Code First

I have two entities in my Core MVC application. There are two Bolge Id in the BolgeToBolge entity; one of them is FromBolgeFk, the other one is ToBolgeFk.
After update-database command in package manager console of Visual Studio 2019, I have an error like this:
Introducing FOREIGN KEY constraint 'FK_BolgeToBolge_Bolge_ToBolgeFk' on table 'BolgeToBolge' 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 codes:
Bolge.cs Entity Model:
public class Bolge
{
[Key]
public int Id { get; set; }
[Display(Name ="Bölge")]
[StringLength(100)]
[Required]
public string BolgeAd { get; set; }
[ForeignKey("Ilce")]
public int IlceFk { get; set; }
public virtual Ilce Ilce { get; set; }
public virtual ICollection<BolgeToBolge> BolgeToBolgeFroms { get; set; }
public virtual ICollection<BolgeToBolge> BolgeToBolgeToes { get; set; }
public virtual ICollection<Rezervasyon> RezervasyonsFrom { get; set; }
public virtual ICollection<Rezervasyon> RezervasyonsTo { get; set; }
}
BolgeToBolge Entity:
public class BolgeToBolge
{
[Key]
public int Id { get; set; }
[ForeignKey("FromBolge")]
[InverseProperty("BolgeToBolgeFroms")]
public int FromBolgeFk { get; set; }
public virtual Bolge FromBolge { get; set; }
[ForeignKey("ToBolge")]
[InverseProperty("BolgeToBolgeToes")]
public int ToBolgeFk { get; set; }
public virtual Bolge ToBolge { get; set; }
}
ApplicationDbContext:DbContext
{
modelBuilder.Entity<BolgeToBolge>()
.HasKey(BtB=> new {BtB.FromBolgeFk, BtB.ToBolgeFk });
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.FromBolge)
.WithMany(BtB => BtB.BolgeToBolgeFroms)
.HasForeignKey(BtB => BtB.FromBolgeFk).OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.ToBolge)
.WithMany(BtB => BtB.BolgeToBolgeToes)
.HasForeignKey(BtB => BtB.ToBolgeFk).OnDelete(DeleteBehavior.Cascade);
}
How can I solve this problem?
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.ToBolge)
.WithMany(BtB => BtB.BolgeToBolgeToes)
.HasForeignKey(BtB => BtB.ToBolgeFk).OnDelete(DeleteBehavior.NoAction);

How can I name a navigation property a different name from it's entity name in my EF POCO?

I have a POCO Entity named Employee.
And then I have a second POCO Entity named Case.
I want a navigation property that looks like instead this:
public class Case : BaseEntity
{
public long EmployeeId { get; set; }
public virtual Employee Employee{ get; set; }
like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
public virtual Employee InitialContact { get; set; }
I want to name my property InitialContact. Not Employee.
But I get this error when EF tries to create the Database:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 1:
I got it to work like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
The primary key is ID in my BaseEntity. Not EmployeeId.
But I have second part to my question.
Here is my Complete Employee POCO:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Hrsa.Core.Generic.Model.Framework.Concrete;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Hrsa.Core.Generic.Model.Lerd
{
public class Employee : BaseEntity
{
[BindNever]
public string Email { get; set; }
[BindNever]
public long OrganizationId { get; set; }
[BindNever]
public string Supervisor { get; set; }
[BindNever]
public string SupervisorEmail { get; set; }
[BindNever]
public string FirstName { get; set; }
[BindNever]
public string LastName { get; set; }
public string Notes { get; set; }
[BindNever]
public long BargainingUnitId { get; set; }
[BindNever]
public long PayPlanId { get; set; }
[BindNever]
public long GradeRankId { get; set; }
[BindNever]
public long PositionTitleId { get; set; }
[BindNever]
public long SeriesId { get; set; }
public bool IsUnionEmployee { get; set; }
public virtual Organization Organization { get; set; }
public virtual BargainingUnit BargainingUnit { get; set; }
public virtual PayPlan PayPlan { get; set; }
public virtual GradeRank GradeRank { get; set; }
public virtual PositionTitle PositionTitle { get; set; }
public virtual Series Series { get; set; }
public virtual ICollection<UnionHours> UnionHours { get; set; }
public virtual ICollection<Case> Cases { get; set; }
[NotMapped]
public string UnionEmployeeYesNo => (IsUnionEmployee) ? "Yes" : "No";
}
}
I want my Employee to have many Cases:
public virtual ICollection<Case> Cases { get; set; }
Here is my complete Cases POCO:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
So now my DB looks like this:
So I have my InitialContactId in Cases ok.
But now I need my Case to have many Employees.
So I add this in to my Case POCO:
public virtual ICollection<Employee> Employees { get; set; }
Now it looks like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Now when I run it, I get this error again:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 2:
I found this article for a Many-Many relationship in .Net Core 1:
http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
So now I have a bridge lookup entity:
public class EmployeeCase
{
[ForeignKey("Id")]
public long EmployeeId { get; set; }
public Employee Employee { get; set; }
[ForeignKey("Id")]
public long CaseId { get; set; }
public Case Case { get; set; }
}
Employee POCO:
Changed:
public virtual ICollection<Case> Cases { get; set; }
to:
// Mapping - Collection of Cases
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
Case POCO:
Changed:
public virtual ICollection<Employee> Employees { get; set; }
to:
// Mapping - Collection of Employees
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
In my AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
}
Now when I run I get this error:
An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code
Additional information: Introducing FOREIGN KEY constraint 'FK_EmployeeCase_Employees_EmployeeId' on table 'EmployeeCase' 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.
Update 3:
Finally got my tables the way I want with this piece of code from:
Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Get rid of Cascading Circular error on ModelBuilding
foreach (var relationShip in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationShip.DeleteBehavior = DeleteBehavior.Restrict;
}
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
base.OnModelCreating(modelBuilder);
}
Update 4:
This did not work after all.
Remvoving the delete behavior for everything messes up my other relationships and I get errors.
How can I fix this?
This is disgusting.
So wishing I did not go Core.
Entity Framework uses conventions to guess how to map your C# model to database objects.
In your case you violate convention by custom name, so you should explain Entity Framework how to map this stuff.
There are two possible ways: attributes and fluent API. I'd suggest to use the latter one.
See section "Configuring a Foreign Key Name That Does Not Follow the Code First Convention" here: Entity Framework Fluent API - Relationships
I have made it a habit of explicitly defining my relationships as EF does not always get them the way I want. I like to create a Mapping folder that contains my entity maps. The fluent api works great for this and inherits from EntityTypeConfiguration.
Try this.
public class CaseMap : EntityTypeConfiguration<Case>
{
public CaseMap()
{
HasKey(m => m.Id)
HasRequired(m => m.InitialContact)
.WithMany(e => e.Cases)
.HasForeignKey(m => m.InitialContactId);
}
}
Almost forgot. You need to tell your DbContext where to find these mappings. Add this to your DbContexts OnModelCreating method.
modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
This is what worked finally for the Cascading Delete circular references on the many-to-many in EF Core:
// Get rid of Cascading Delete Circular references error.
var type = modelBuilder.Model.GetEntityTypes().Single(t => t.Name == "Hrsa.Core.Generic.Model.Lerd.EmployeeCase");
foreach (var relationship in type.GetForeignKeys())
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
You have to get the Entity representing the many to many lookup only.
And from there restrict the DeleteBehavior.

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

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

Is there a data annotation equivalent to Entity Framework's fluent API's WillCascadeOnDelete()? [duplicate]

I'm currently using EF Code First 4.3 with migrations enabled, but automatic migrations disabled.
My question is simple, is there a data annotations equivalent of the model configuration .WillCascadeOnDelete(false)
I would like to decorate my class so that the foreign key relationships do NOT trigger a cascading delete.
Code sample:
public class Container
{
public int ContainerID { get; set; }
public string Name { get; set; }
public virtual ICollection<Output> Outputs { get; set; }
}
public class Output
{
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
public class OutputType
{
public int OutputTypeID { get; set; }
public string Name { get; set; }
}
I Would like to do something like this:
public class Output
{
[CascadeOnDelete(false)]
public int ContainerID { get; set; }
public virtual Container Container { get; set; }
[CascadeOnDelete(false)]
public int OutputTypeID { get; set; }
public virtual OutputType OutputType { get; set; }
public int Quantity { get; set; }
}
This way i would be able to scaffold the migration correctly. which scaffolds the foreign key relationships to be cascade deleted at the moment.
Any ideas, other than using Model Configuration?
No there is no such equivalent. You must use fluent API to remove cascade delete selectively or you must remove OneToManyCascadeDelete convention to remove it globally.
Create a mapping class (the fluent syntax) and use the code below:
// add relationships "Post" and "User" to a "Comment" entity
this.HasRequired(t => t.Post)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.PostID)
.WillCascadeOnDelete(false); // <---
this.HasOptional(t => t.User)
.WithMany(t => t.Comments)
.HasForeignKey(d => d.UserID)
.WillCascadeOnDelete(false); // <---
Here's a nice post on how to set up fluent mappings if you need more info.
Just make the FK property nullable can prevent cascade delete from happening:
public int? OutputTypeID { get; set; }