The INSERT statement conflicted with the FOREIGN KEY constraint . The statement has been terminated - entity-framework

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_T_OrderDetails_T_SelectionList". The conflict occurred in database "AlbumPortal", table "dbo.T_SelectionList", column 'ID'.
I am new to ASP.NET MVC with EF code-first approach, I get this error when trying to save data to T_OrderDetails from controller.
In T_OrderDetails table 'SelectionList_ID' is foreign key from the table dbo.T_SelectionList. For that it is defined as
public virtual T_SelectionList T_SelectionList { get; set; }
in 'T_OrderDetails' and
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<T_OrderDetails> T_OrderDetails { get; set; }
in 'T_SelectionList'. Also
modelBuilder.Entity<T_SelectionList>()
.HasMany(e => e.T_OrderDetails)
.WithOptional(e => e.T_SelectionList)
.HasForeignKey(e => e.SelectionList_ID);
is defined. Then I don't understand why this error getting.

Related

Entity framework computed primary key issue

I have issue with computed primary key.
In table class I have this property. It is primary computed key.
[Key]
[StringLength(24)]
public string TagId { get; set; } = null!;
In context class I have
entity.Property(e => e.TagId).HasComputedColumnSql("(CONVERT([nvarchar](24),([TagCd]+'.')+case when len([Nbr])=(1) then '0'+CONVERT([nvarchar](4),[Nbr]) else CONVERT([nvarchar](4),[Nbr]) end))", true);
In OnModelCreating method
modelBuilder.Entity<Tag>(entity =>
{
entity.Property(e => e.TagId).ValueGeneratedOnAdd();
});
When I try to save new record entity framewrok still add primary key to insert query and then I get exception
SqlException: The column TagId cannot be modified because it is either a computed column or is the result of a UNION operator
Can someone help me ? What should I do ? It is database first approach.

EF Core relationship throwing error when creating the database

I have an Address class, that I use in a Customer class and in an Order class:
public class Address
{
public Customer Customer { get; set }
...
}
public class Customer
{
...
public List<Address> Addresses { get; set;}
}
public class Order
{
...
public Customer Curstomer { get; set; }
public Address BillingAddress { get; set;}
public Address ShippingAddress { get; set;}
}
I created the migrations succesfully but when I try to update-database I get the following error:
Introducing FOREIGN KEY constraint
'FK_Order_Address_ShippingAddressId' on table 'Order' 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.
What is the correct way of setting up such relationship? Is this modelling even correct? Because it seems strange to me that Address is a property related to Customer, but I'm also using it on Order, but duplicating the addresses in an OrderAddresses table seems wrong too.
To fix this, change the ReferentialAction for the onDelete of your foreign key in the migration to something other than Cascade. Restrict is probably a good option. It will look something like this:
constraints: table =>
{
table.PrimaryKey("PK_Order", x => x.Id);
table.ForeignKey(
name: "FK_Orders_BillingAddress_BillingAddressId",
column: x => x.BillingAddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
Just change that to Restrict or another choice.
Why?
If you have Cascade, suppose you were to delete the BillingAddress of your Order. That would try to cascade the delete to the Order which would then cascade its delete to the ShippingAddress which would then try to cascade that delete back to the Order and so on and so forth, hence why SQL Server correctly errors out on cyclical cascading deletes.
See also this question: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?

Update optional FK to Required with Automatic Migrations

I have a table which has an optional FK to another table and want to change that FK to a required relationship.
I have Automatic Migrations enabled and enabled destructive changes for this update. All entities in the database also have this key populated.
I changed this:
modelBuilder.Entity<Blog>().HasOptional(b => b.AuthorSecurable).WithMany().Map(b => b.MapKey("AuthorSecurableId"));
to:
modelBuilder.Entity<Blog>().HasRequired(b => b.AuthorSecurable).WithMany().Map(b => b.MapKey("AuthorSecurableId"));
and got the following error:
'FK_dbo.Blogs_dbo.Securables_AuthorSecurableId' is not a constraint.
Could not drop constraint. See previous errors.
There are no previous errors I could see (no inner exception ect.)
This post says you can get around this error with the following:
ALTER TABLE [dbo].[Blogs] NOCHECK CONSTRAINT [FK_dbo.Blogs_dbo.Securables_AuthorSecurable_Id]
so i did:
public override void Up()
{
Sql("ALTER TABLE [dbo].[Blogs] NOCHECK CONSTRAINT [FK_dbo.Blogs_dbo.Securables_AuthorSecurable_Id]");
DropForeignKey("dbo.Blogs", "AuthorSecurableId", "dbo.Securables");
DropIndex("dbo.Blogs", new[] { "AuthorSecurableId" });
AlterColumn("dbo.Blogs", "AuthorSecurableId", c => c.Int(nullable: false));
AddForeignKey("dbo.Blogs", "AuthorSecurableId", "dbo.Securables", "Id", cascadeDelete: true);
CreateIndex("dbo.Blogs", "AuthorSecurableId");
}
But still got the same error
EDIT:
the full code is avaliable here and a minimal models are below:
public class Blog
{
public int Id { get; set; }
public Securable AuthorSecurable { get; set; }
}
public class Securable
{
public int Id { get; set; }
}

EntityFramework: How to configure Cascade-Delete to nullify Foreign Keys

EntityFramework's documentation states that the following behavior is possible:
If a foreign key on the dependent entity is nullable, Code First does
not set cascade delete on the relationship, and when the principal is
deleted the foreign key will be set to null.
(from http://msdn.microsoft.com/en-us/jj591620)
However, I cannot achieve such a behavior.
I have the following Entities defined with code-first:
public class TestMaster
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TestChild> Children { get; set; }
}
public class TestChild
{
public int Id { get; set; }
public string Name { get; set; }
public virtual TestMaster Master { get; set; }
public int? MasterId { get; set; }
}
Here is the Fluent API mapping configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TestMaster>()
.HasMany(e => e.Children)
.WithOptional(p => p.Master).WillCascadeOnDelete(false);
modelBuilder.Entity<TestChild>()
.HasOptional(e => e.Master)
.WithMany(e => e.Children)
.HasForeignKey(e => e.MasterId).WillCascadeOnDelete(false);
}
Foreign Key is nullable, navigation property is mapped as Optional, so I expect the cascade delete to work as described as MSDN - i.e. to nullify MasterID's of all children and then delete the Master object.
But when I actually try to delete, I get the FK violation error:
using (var dbContext = new TestContext())
{
var master = dbContext.Set<TestMaster>().Find(1);
dbContext.Set<TestMaster>().Remove(master);
dbContext.SaveChanges();
}
On SaveChanges() it throws the following:
System.Data.Entity.Infrastructure.DbUpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.UpdateException : An error occurred while updating the entries. See the inner exception for details.
----> System.Data.SqlClient.SqlException : The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.TestChilds_dbo.TestMasters_MasterId". The conflict occurred in database "SCM_Test", table "dbo.TestChilds", column 'MasterId'.
The statement has been terminated.
Am I doing something wrong or did I misunderstood what the MSDN says?
It works indeed as described but the article on MSDN misses to emphasize that it only works if the children are loaded into the context as well, not only the parent entity. So, instead of using Find (which only loads the parent) you must use eager loading with Include (or any other way to load the children into the context):
using (var dbContext = new TestContext())
{
var master = dbContext.Set<TestMaster>().Include(m => m.Children)
.SingleOrDefault(m => m.Id == 1);
dbContext.Set<TestMaster>().Remove(master);
dbContext.SaveChanges();
}
This will delete the master from the database, set all foreign keys in the Child entities to null and write UPDATE statements for the children to the database.
After following #Slauma's great answer I was still getting same error as OP.
So don't be as naive as me and think that the examples below will end up with same result.
dbCtx.Entry(principal).State = EntityState.Deleted;
dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
// code above will give error and code below will work on dbCtx.SaveChanges()
dbCtx.Dependant.Where(d => d.PrincipalId == principalId).Load();
dbCtx.Entry(principal).State = EntityState.Deleted;
First load the children into context before setting entity state to deleted (if you are doing it that way).

EF and character PK/FK

I am a newbie in the EF. I read http://msdn.microsoft.com/en-us/data/gg193958.aspx and still confused.
I have an existing database and I'm writing a Code First model. I have Operators table with op_code Char(6) Primary Key. In the Operator class I named it OperatorCode, e.g.
[Key]
[Column("op_code",TypeName = "char")]
[DisplayName("Operator")]
public virtual string OperatorCode { get; set; }
In several of my tables I have EnteredBy and in some ModifiedBy columns that are FK to the op_code.
Say, for the Clients table I have both of these fields.
So, I added to the Operator class at the bottom:
[InverseProperty("EnteredBy")]
public virtual ICollection<Client> ClientsEnteredBy { get; set; }
[InverseProperty("ModifiedBy")]
public virtual ICollection<Client> ClientsUpdatedBy { get; set; }
and I added the following into the Client class:
public virtual Operator EnteredBy { get; set; }
public virtual Operator ModifiedBy { get; set; }
and I am getting a run-time error about EnteredBy_OperatorCode and ModifiedBy_OperatorCode columns.
What should I fix /add to let EF know my column names?
Thanks in advance.
Your foreign column names in the database do not match the default convention for FK names which is NavigationPropertyName_PrimaryKeyNameinTargetClass. Because your navigation properties are called EnteredBy and ModifiedBy and the primary key property is called OperatorCode EF expects - according to the mentioned convention - EnteredBy_OperatorCode and ModifiedBy_OperatorCode as foreign key columns. But those do not exist in the database which is the reason for your exception. Instead your FK columns are EnteredBy and ModifiedBy.
So, to fix the problem you must override the convention.
If you don't have FK properties in your model use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredBy) // or HasOptional
.Map(m => m.MapKey("EnteredBy")); // mapping for the FK column name
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedBy) // or HasOptional
.Map(m => m.MapKey("ModifiedBy")); // mapping for the FK column name
(With this mapping you can remove the InverseProperty attribute.)
An alternative approach is to expose the FKs as properties in the model. Rename the navigation properties and use their names for the FK properties. The mapping is then possible with data annotations.
In Client class:
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
And remove the InverseProperty attributes in the Operator class.
Instead of the data annotations you can also use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredByOperator) // or HasOptional
.HasForeignKey(c => c.EnteredBy);
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedByOperator) // or HasOptional
.HasForeignKey(c => c.ModifiedBy);
If both relationships are required you will need to disable cascading delete for at least one of the relationships (append .WillCascadeOnDelete(false) at the end of one of the mappings), otherwise SQL Server will throw an error that multiple cascading delete paths between the tables are not allowed.
I would suggest to use the "alternative approach" (expose foreign keys as properties) because it is easier to work with in most cases.