Entity Framework "A relationship multiplicity constraint violation occurred" - c#

I received this error from Entity Framework this morning:
A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
The error refers to a very simple One-To-Many relationship between User and Address
public class User
{
public Guid Id { get; set; }
public virtual IList<Address> Addresses { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public User User { get; set; }
}
I'm using Fluent API to configure the relationship between entities, but in this case the relationship seems to simple that at first I didn't specify any particular rule and I let EF "deduce" the relationship:
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
HasKey(a => a.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
}
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(u => u.Id);
Property(u => u.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
}
}
But after receiving the error I tried to specify the following rule in the AddressConfiguration:
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
HasKey(a => a.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
HasOptional(x => x.User).WithMany(); // I tried adding this
}
}
After doing that, I tried generating a new automatic migration, and this is what I obtained:
public partial class AddressFixMigration : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.Addresses", "User_Id", "dbo.Users");
AddColumn("dbo.Addresses", "User_Id1", c => c.Guid());
CreateIndex("dbo.Addresses", "User_Id1");
AddForeignKey("dbo.Addresses", "User_Id1", "dbo.Users", "Id");
}
public override void Down()
{
DropForeignKey("dbo.Addresses", "User_Id1", "dbo.Users");
DropIndex("dbo.Addresses", new[] { "User_Id1" });
DropColumn("dbo.Addresses", "User_Id1");
AddForeignKey("dbo.Addresses", "User_Id", "dbo.Users", "Id");
}
}
I found this very odd. The Db seemed ok even before, with the Addresses Table having a Foreign Key "User_Id", but after specifying the one-to-many relationship in the Configuration file EF wants to create a different Foreign Key. Why??
I also tried to specify HasOptional(x => x.User).WithMany().Map(x => x.MapKey("User_Id"));, but in that case when I try to create the automatic migration I receive the following error:
User_Id: Name: Each property name in a type must be unique. Property name 'User_Id' is already defined.
It seems like something is clearly wrong with my DB, but I can't see what and why.

There are other techniques, but I prefer to put the FK in explicitly:
public class Address
{
public Guid Id { get; set; }
public Int? User_Id { get; set; }
public User User { get; set; }
}
Then use this fluent code:
HasOptional(x => x.User).WithMany(x => x.Addresses).HasForeignKey(x => x.User_Id);

I realized that I needed to change my Configuration file as it follows:
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
HasKey(a => a.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
HasOptional(x => x.User).WithMany(x => x.Addresses).Map(x => x.MapKey("User_Id"));
}
}
.WithMany() (without parameter) is for when there is no navigation property on the other side of the relationship, while in this case I needed to spacify .WithMany(x => x.Addresses) because User actually contains a list of Addresses.

Related

Introducing foreign key constraint asp.net problem [duplicate]

I've been wrestling with this for a while and can't quite figure out what's happening. I have a Card entity which contains Sides (usually 2) - and both Cards and Sides have a Stage. I'm using EF Codefirst migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_dbo.Sides_dbo.Cards_CardId' on
table 'Sides' may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Here's my Card entity:
public class Card
{
public Card()
{
Sides = new Collection<Side>();
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int CardId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
[ForeignKey("CardId")]
public virtual ICollection<Side> Sides { get; set; }
}
Here's my Side entity:
public class Side
{
public Side()
{
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int SideId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
public int CardId { get; set; }
[ForeignKey("CardId")]
public virtual Card Card { get; set; }
}
And here's my Stage entity:
public class Stage
{
// Zero
public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
// Ten seconds
public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");
public static IEnumerable<Stage> Values
{
get
{
yield return ONE;
yield return TWO;
}
}
public int StageId { get; set; }
private readonly TimeSpan span;
public string Title { get; set; }
Stage(TimeSpan span, string title)
{
this.span = span;
this.Title = title;
}
public TimeSpan Span { get { return span; } }
}
What's odd is that if I add the following to my Stage class:
public int? SideId { get; set; }
[ForeignKey("SideId")]
public virtual Side Side { get; set; }
The migration runs successfully. If I open up SSMS and look at the tables, I can see that Stage_StageId has been added to Cards (as expected/desired), however Sides contains no reference to Stage (not expected).
If I then add
[Required]
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int StageId { get; set; }
To my Side class, I see StageId column added to my Side table.
This is working, but now throughout my application, any reference to Stage contains a SideId, which is in some cases totally irrelevant. I'd like to just give my Card and Side entities a Stage property based on the above Stage class without polluting the stage class with reference properties if possible... what am I doing wrong?
Because Stage is required, all one-to-many relationships where Stage is involved will have cascading delete enabled by default. It means, if you delete a Stage entity
the delete will cascade directly to Side
the delete will cascade directly to Card and because Card and Side have a required one-to-many relationship with cascading delete enabled by default again it will then cascade from Card to Side
So, you have two cascading delete paths from Stage to Side - which causes the exception.
You must either make the Stage optional in at least one of the entities (i.e. remove the [Required] attribute from the Stage properties) or disable cascading delete with Fluent API (not possible with data annotations):
modelBuilder.Entity<Card>()
.HasRequired(c => c.Stage)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Side>()
.HasRequired(s => s.Stage)
.WithMany()
.WillCascadeOnDelete(false);
I had a table that had a circular relationship with others and I was getting the same error. Turns out it is about the foreign key which was not nullable. If the key is not nullable the related object must be deleted, and circular relations don't allow that. So use nullable foreign key.
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
Anybody wondering how to do it in EF core:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
..... rest of the code.....
I was getting this error for lots of entities when I was migrating down from an EF7 model to an EF6 version. I didn't want to have to go through each entity one at a time, so I used:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
You can set cascadeDelete to false or true (in your migration Up() method). Depends upon your requirement.
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
In .NET Core I changed the onDelete option to ReferencialAction.NoAction
constraints: table =>
{
table.PrimaryKey("PK_Schedule", x => x.Id);
table.ForeignKey(
name: "FK_Schedule_Teams_HomeId",
column: x => x.HomeId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_Schedule_Teams_VisitorId",
column: x => x.VisitorId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});
I had this issue also, I solved it instantly with this answer from a similar thread
In my case, I didn't want to delete the dependent record on key deletion. If this is the case in your situation just simply change the Boolean value in the migration to false:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Chances are, if you are creating relationships which throw this compiler error but DO want to maintain cascade delete; you have an issue with your relationships.
I fixed this. When you add the migration, in the Up() method there will be a line like this:
.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
If you just delete the cascadeDelete from the end it will work.
Just for documentation purpose, to someone that comes on the future, this thing can be solved as simple as this, and with this method, you could do a method that disabled one time, and you could access your method normally
Add this method to the context database class:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
In .NET Core I played with all upper answers - but without any success.
I made changes a lot in DB structure and every time added new migration attempting to update-database, but received the same error.
Then I started to remove-migration one by one until Package Manager Console threw me exception:
The migration '20170827183131_***' has already been applied to the database
After that, I added new migration (add-migration) and update-database successfully
So my suggestion would be: clear out all your temp migrations, until your current DB state.
public partial class recommended_books : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.RecommendedBook",
c => new
{
RecommendedBookID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
DepartmentID = c.Int(nullable: false),
Title = c.String(),
Author = c.String(),
PublicationDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.RecommendedBookID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: false) // was true on migration
.ForeignKey("dbo.Department", t => t.DepartmentID, cascadeDelete: false) // was true on migration
.Index(t => t.CourseID)
.Index(t => t.DepartmentID);
}
public override void Down()
{
DropForeignKey("dbo.RecommendedBook", "DepartmentID", "dbo.Department");
DropForeignKey("dbo.RecommendedBook", "CourseID", "dbo.Course");
DropIndex("dbo.RecommendedBook", new[] { "DepartmentID" });
DropIndex("dbo.RecommendedBook", new[] { "CourseID" });
DropTable("dbo.RecommendedBook");
}
}
When your migration fails you are given a couple of options:
'Introducing FOREIGN KEY constraint 'FK_dbo.RecommendedBook_dbo.Department_DepartmentID' on table 'RecommendedBook' 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.'
Here is an example of using the 'modify other FOREIGN KEY constraints' by setting 'cascadeDelete' to false in the migration file and then run 'update-database'.
Make your Foreign key attributes nullable. That will work.
This sounds weird and I don't know why, but in my case that was happening because my ConnectionString was using "." in "data source" attribute. Once I changed it to "localhost" it workded like a charm. No other change was needed.
The existing answers are great I just wanted to add that I ran into this error because of a different reason. I wanted to create an Initial EF migration on an existing DB but I didn't use the -IgnoreChanges flag and applied the Update-Database command on an empty Database (also on the existing fails).
Instead I had to run this command when the current db structure is the current one:
Add-Migration Initial -IgnoreChanges
There is likely a real problem in the db structure but save the world one step at a time...
In .NET 5 < and .NET Core 2.0 < you can use .OnDelete(DeleteBehavior.Restrict) in OnModelCreating like #Nexus23 answer but you do not need to disable cascade for every model.
Example with join entity type configuration many-to-many:
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Restrict),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.Restrict),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Sources:
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
This does require you to remove the many to many relationship yourself or you will receive the following error when you remove a parent entity:
The association between entity types '' and '' has been severed, but
the relationship is either marked as required or is implicitly
required because the foreign key is not nullable. If the
dependent/child entity should be deleted when a required relationship
is severed, configure the relationship to use cascade deletes.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to
see the key values
You can solve this by using DeleteBehavior.ClientCascade instead which will allow EF to perform cascade deletes on loaded entities.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.ClientCascade),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
None of the aforementioned solutions worked for me. What I had to do was use a nullable int (int?) on the foreign key that was not required (or not a not null column key) and then delete some of my migrations.
Start by deleting the migrations, then try the nullable int.
Problem was both a modification and model design. No code change was necessary.
The simple way is to, Edit your migration file (cascadeDelete: true) into (cascadeDelete: false) then after assign the Update-Database command in your Package Manager Console.if it's problem with your last migration then all right. Otherwise check your earlier migration history, copy those things, paste into your last migration file, after that do it the same thing. it perfectly works for me.
You could add this in your DataContext.cs, this works for me...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
I ran into the same problem and stuck for a long. The following steps saved me.
Go through the constraints and change the onDelete ReferentialAction to NoAction from Cascade
constraints: table =>
{
table.PrimaryKey("PK_table1", x => x.Id);
table.ForeignKey(
name: "FK_table1_table2_table2Id",
column: x => x.table2Id,
principalTable: "table2",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});

Introducing FOREIGN KEY constraint may cause cycles or multiple cascade [duplicate]

I've been wrestling with this for a while and can't quite figure out what's happening. I have a Card entity which contains Sides (usually 2) - and both Cards and Sides have a Stage. I'm using EF Codefirst migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_dbo.Sides_dbo.Cards_CardId' on
table 'Sides' may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Here's my Card entity:
public class Card
{
public Card()
{
Sides = new Collection<Side>();
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int CardId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
[ForeignKey("CardId")]
public virtual ICollection<Side> Sides { get; set; }
}
Here's my Side entity:
public class Side
{
public Side()
{
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int SideId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
public int CardId { get; set; }
[ForeignKey("CardId")]
public virtual Card Card { get; set; }
}
And here's my Stage entity:
public class Stage
{
// Zero
public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
// Ten seconds
public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");
public static IEnumerable<Stage> Values
{
get
{
yield return ONE;
yield return TWO;
}
}
public int StageId { get; set; }
private readonly TimeSpan span;
public string Title { get; set; }
Stage(TimeSpan span, string title)
{
this.span = span;
this.Title = title;
}
public TimeSpan Span { get { return span; } }
}
What's odd is that if I add the following to my Stage class:
public int? SideId { get; set; }
[ForeignKey("SideId")]
public virtual Side Side { get; set; }
The migration runs successfully. If I open up SSMS and look at the tables, I can see that Stage_StageId has been added to Cards (as expected/desired), however Sides contains no reference to Stage (not expected).
If I then add
[Required]
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int StageId { get; set; }
To my Side class, I see StageId column added to my Side table.
This is working, but now throughout my application, any reference to Stage contains a SideId, which is in some cases totally irrelevant. I'd like to just give my Card and Side entities a Stage property based on the above Stage class without polluting the stage class with reference properties if possible... what am I doing wrong?
Because Stage is required, all one-to-many relationships where Stage is involved will have cascading delete enabled by default. It means, if you delete a Stage entity
the delete will cascade directly to Side
the delete will cascade directly to Card and because Card and Side have a required one-to-many relationship with cascading delete enabled by default again it will then cascade from Card to Side
So, you have two cascading delete paths from Stage to Side - which causes the exception.
You must either make the Stage optional in at least one of the entities (i.e. remove the [Required] attribute from the Stage properties) or disable cascading delete with Fluent API (not possible with data annotations):
modelBuilder.Entity<Card>()
.HasRequired(c => c.Stage)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Side>()
.HasRequired(s => s.Stage)
.WithMany()
.WillCascadeOnDelete(false);
I had a table that had a circular relationship with others and I was getting the same error. Turns out it is about the foreign key which was not nullable. If the key is not nullable the related object must be deleted, and circular relations don't allow that. So use nullable foreign key.
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
Anybody wondering how to do it in EF core:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
..... rest of the code.....
I was getting this error for lots of entities when I was migrating down from an EF7 model to an EF6 version. I didn't want to have to go through each entity one at a time, so I used:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
You can set cascadeDelete to false or true (in your migration Up() method). Depends upon your requirement.
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
In .NET Core I changed the onDelete option to ReferencialAction.NoAction
constraints: table =>
{
table.PrimaryKey("PK_Schedule", x => x.Id);
table.ForeignKey(
name: "FK_Schedule_Teams_HomeId",
column: x => x.HomeId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_Schedule_Teams_VisitorId",
column: x => x.VisitorId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});
I had this issue also, I solved it instantly with this answer from a similar thread
In my case, I didn't want to delete the dependent record on key deletion. If this is the case in your situation just simply change the Boolean value in the migration to false:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Chances are, if you are creating relationships which throw this compiler error but DO want to maintain cascade delete; you have an issue with your relationships.
I fixed this. When you add the migration, in the Up() method there will be a line like this:
.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
If you just delete the cascadeDelete from the end it will work.
Just for documentation purpose, to someone that comes on the future, this thing can be solved as simple as this, and with this method, you could do a method that disabled one time, and you could access your method normally
Add this method to the context database class:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
In .NET Core I played with all upper answers - but without any success.
I made changes a lot in DB structure and every time added new migration attempting to update-database, but received the same error.
Then I started to remove-migration one by one until Package Manager Console threw me exception:
The migration '20170827183131_***' has already been applied to the database
After that, I added new migration (add-migration) and update-database successfully
So my suggestion would be: clear out all your temp migrations, until your current DB state.
public partial class recommended_books : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.RecommendedBook",
c => new
{
RecommendedBookID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
DepartmentID = c.Int(nullable: false),
Title = c.String(),
Author = c.String(),
PublicationDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.RecommendedBookID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: false) // was true on migration
.ForeignKey("dbo.Department", t => t.DepartmentID, cascadeDelete: false) // was true on migration
.Index(t => t.CourseID)
.Index(t => t.DepartmentID);
}
public override void Down()
{
DropForeignKey("dbo.RecommendedBook", "DepartmentID", "dbo.Department");
DropForeignKey("dbo.RecommendedBook", "CourseID", "dbo.Course");
DropIndex("dbo.RecommendedBook", new[] { "DepartmentID" });
DropIndex("dbo.RecommendedBook", new[] { "CourseID" });
DropTable("dbo.RecommendedBook");
}
}
When your migration fails you are given a couple of options:
'Introducing FOREIGN KEY constraint 'FK_dbo.RecommendedBook_dbo.Department_DepartmentID' on table 'RecommendedBook' 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.'
Here is an example of using the 'modify other FOREIGN KEY constraints' by setting 'cascadeDelete' to false in the migration file and then run 'update-database'.
Make your Foreign key attributes nullable. That will work.
This sounds weird and I don't know why, but in my case that was happening because my ConnectionString was using "." in "data source" attribute. Once I changed it to "localhost" it workded like a charm. No other change was needed.
The existing answers are great I just wanted to add that I ran into this error because of a different reason. I wanted to create an Initial EF migration on an existing DB but I didn't use the -IgnoreChanges flag and applied the Update-Database command on an empty Database (also on the existing fails).
Instead I had to run this command when the current db structure is the current one:
Add-Migration Initial -IgnoreChanges
There is likely a real problem in the db structure but save the world one step at a time...
In .NET 5 < and .NET Core 2.0 < you can use .OnDelete(DeleteBehavior.Restrict) in OnModelCreating like #Nexus23 answer but you do not need to disable cascade for every model.
Example with join entity type configuration many-to-many:
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Restrict),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.Restrict),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Sources:
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
This does require you to remove the many to many relationship yourself or you will receive the following error when you remove a parent entity:
The association between entity types '' and '' has been severed, but
the relationship is either marked as required or is implicitly
required because the foreign key is not nullable. If the
dependent/child entity should be deleted when a required relationship
is severed, configure the relationship to use cascade deletes.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to
see the key values
You can solve this by using DeleteBehavior.ClientCascade instead which will allow EF to perform cascade deletes on loaded entities.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.ClientCascade),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
None of the aforementioned solutions worked for me. What I had to do was use a nullable int (int?) on the foreign key that was not required (or not a not null column key) and then delete some of my migrations.
Start by deleting the migrations, then try the nullable int.
Problem was both a modification and model design. No code change was necessary.
The simple way is to, Edit your migration file (cascadeDelete: true) into (cascadeDelete: false) then after assign the Update-Database command in your Package Manager Console.if it's problem with your last migration then all right. Otherwise check your earlier migration history, copy those things, paste into your last migration file, after that do it the same thing. it perfectly works for me.
You could add this in your DataContext.cs, this works for me...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
I ran into the same problem and stuck for a long. The following steps saved me.
Go through the constraints and change the onDelete ReferentialAction to NoAction from Cascade
constraints: table =>
{
table.PrimaryKey("PK_table1", x => x.Id);
table.ForeignKey(
name: "FK_table1_table2_table2Id",
column: x => x.table2Id,
principalTable: "table2",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});

Entity Framework 6 foreign key and target column of different types

recently a sql server GIS database was updated which changed several data types.
I now have table named Trail with a Column named Status of type Numeric(40,0) this is to be a foreign key to Table named Trail_Status with column named Status_Key (int)
here was my attempt.
public class TrailConfiguration : EntityTypeConfiguration<TrailModel>
{
public TrailConfiguration()
{
this.ToTable("PA_TRAILS", "dbo");
this.Property(p => p.StatusKey)
.HasColumnName("STATUS")
.HasColumnType("numeric")
.IsRequired();
this.HasRequired(p => p.Status)
.WithMany()
.HasForeignKey(k => k.StatusKey);
}
}
public class TrailModel
{
[Column(TypeName = "numeric")]
public int StatusKey { get; set; }
public virtual TrailStatusModel Status { get; set; }
}
public TrailStatusConfiguration()
{
this.ToTable("trail_status", "dbo");
this.HasKey(k => k.StatusKey);
this.Property(p => p.StatusKey)
.HasColumnName("status_key")
.HasColumnType("int")
.IsRequired();
this.Property(p => p.Name)
.HasColumnName("name")
.HasColumnType("nvarchar")
.IsRequired();
}
public class TrailStatusModel
{
public int StatusKey { get; set; }
public string Name { get; set; }
}
I receive the following error.
TrailStatusModel_TrailModel: : The types of all properties in the
Dependent Role of a referential constraint must be the same as the
corresponding property types in the Principal Role. The type of
property 'STATUS' on entity 'TrailModel' does not match the type of
property 'status_key' on entity 'TrailStatusModel' in the referential
constraint 'TrailModel_Status'.

TPC in Entity Framework v6

I'm trying to do a pretty simple thing in Entity Framework.
I have a product that has zero or more parameters and these parameters will be mapped to their own tables. However, I'm unable to get this to work. I've been trying to get the mappings right and then use the migrations to see what the database is supposed to look like. I know that this is very simple in NHibernate, but I'm forced against my will to use Entity Framework v6.
Background
These are my entities:
namespace Entities
{
public class EntityState
{
public int Id { get; set; }
}
public class ProductState : EntityState
{
public virtual ICollection<ProductParameterState> Parameters { get; set; }
}
public abstract class ProductParameterState : EntityState
{
}
public class ColorParameterState : ProductParameterState
{
public virtual string Color { get; set; }
}
public class SizeParameterState : ProductParameterState
{
public virtual int Size { get; set; }
}
}
I would like to store this in the following schema:
How to do this?
My attempts
Table-per-class
I tried mapping using TPC:
namespace Mappings
{
public class ProductMap : EntityTypeConfiguration<ProductState>
{
public ProductMap()
{
HasKey(x => x.Id);
Property(x => x.Name);
HasMany(x => x.Parameters);
}
}
public class ColorParameterMap : EntityTypeConfiguration<ColorParameterState>
{
public ColorParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Color);
Map(x =>
{
x.ToTable("ColorParameters");
x.MapInheritedProperties();
});
}
}
public class SizeParameterMap : EntityTypeConfiguration<SizeParameterState>
{
public SizeParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Size);
Map(x =>
{
x.ToTable("SizeParameters");
x.MapInheritedProperties();
});
}
}
}
But this gives the error The association 'ProductState_Parameters' between entity types 'ProductState' and 'ProductParameterState' is invalid. In a TPC hierarchy independent associations are only allowed on the most derived types..
Don't use an inheritence strategy
So I tried to remove the MapInheritedProperties, but then it wants to create an additional, and unwanted, table:
CreateTable(
"dbo.ProductParameterStates",
c => new
{
Id = c.Int(nullable: false, identity: true),
ProductState_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.ProductStates", t => t.ProductState_Id)
.Index(t => t.ProductState_Id);
I don't want this. I am able to get rid of this one by removing the Parameters property in Product, but then I'm not able to use the Parameters of a Product.
Am I asking for too much or is it possible?
You can use TPC, but the relationship must be bidirectional with explicit FK defined (which I guess is the opposite of "independent association" mentioned in the error message).
Add inverse navigation property and FK property to your base entity:
public abstract class ProductParameterState : EntityState
{
public int ProductId { get; set; }
public ProductState Product { get; set; }
}
and use the same entity configurations as in your first attempt, except for the ProductMap where you either remove the following
HasMany(x => x.Parameters);
or change it to
HasMany(e => e.Parameters)
.WithRequired(e => e.Product)
.HasForeignKey(e => e.ProductId);

One to zero-or-one with HasForeignKey

I have two models:
public class Person
{
public virtual int Id { get; set; }
public virtual Employee Employee { get; set; } // optional
}
public class Employee
{
public virtual int Id { get; set; }
public virtual int PersonId { get; set; }
public virtual Person Person {get; set; } // required
}
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
Property(e=>e.PersonId) // I need this property mapped
.HasColumnName("person_id")
.HasColumnType("int");
}
}
I want to map them using fluent mapping. Employee table has column 'person_id' which is non-nullable. I tried following:
HasRequired(e => e.Person)
.WithOptional(p => p.Employee)
.Map(m => m.MapKey("person_id"));
But it fails with:
System.Data.Entity.ModelConfiguration.ModelValidationException : One
or more validation errors were detected during model generation:
person_id: Name: Each property name in a type must be unique. Property
name 'person_id' is already defined.
I need PersonId property on its own, so what I basically want is:
HasRequired(e => e.Person)
.WithOptional(p => p.Employee)
.HasForeignKey(e => e.PersonId); // there is no such method
But there is no such method here as HasForeignKey
OK, I figured that out - you should use WithMany (yep, not obvious) in order to store foreign key in some property:
Property(e => e.PersonId).HasColumnName("person_id");
HasRequired(e => e.Person)
.WithMany()
.HasForeignKey(p => p.PersonId);
See One-to-One Foreign Key Associations article for details. BTW this will create employee foreign key column for person's table, but there is no other way to have navigation property and foreign key separately.
If you need foreign key read-only, you can change PersonId property to:
public int PersonId { get { return Person.Id; } }
And use your original mapping
HasRequired(e => e.Person)
.WithOptional(p => p.Employee)
.Map(m => m.MapKey("person_id"));
There's actually a better way to do this:
HasKey(e => e.PersonId);
HasRequired(e => e.Person)
.WithOptional(p => p.Employee);
Note that you no longer need the Employee.Id property with this approach, as it was superfluous in terms of a 1 to 0 or 1 relationship in the first place.

Categories