EF CodeFirst Mapping Two Cascade Delete Relationships - entity-framework

I have some issues getting cascading to work on my optional one-to-one and one-to-many relation. Enabling 1 of them works fine but enabling both results in a 'possible circular cascading exception'.
I have a 'Customer' which has multiple 'DeliverAddresses' and one 'VisitAddress'. So for address I have an optional DeliverAddressForCustomer and an optional VisitAddressForCustomer.
this results in the following tables:
CREATE TABLE [dbo].[Customer]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY
[Name] NVARCHAR(50) NOT NULL,
)
CREATE TABLE [dbo].[Address]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Street] NVARCHAR(50) NOT NULL,
[DeliverAddressForCustomerId] INT NULL,
[VisitAddressForCustomer] INT NULL,
CONSTRAINT [FK_Address_Customer_DeliverAddressForCustomerId] FOREIGN KEY ([DeliverAddressForCustomerId]) REFERENCES [Customer]([Id]),
CONSTRAINT [FK_Address_Customer_VisitAddressForCustomerId] FOREIGN KEY ([VisitAddressForCustomerId]) REFERENCES [Customer]([Id])
)
This works with the following mapping:
this.ToTable("Address");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Postcode).HasColumnName("Postcode");
this.Property(t => t.DeliverAddressForCustomerId).HasColumnName("DeliverAddressForCustomerId");
// Relationships
this.HasOptional(t => t.DeliverAddressForCustomer)
.WithMany(t => t.DeliverAddresses)
.HasForeignKey(d => d.DeliverAddressForCustomerId)
.WillCascadeOnDelete(true);
this.HasOptional(a => a.VisitAddressForCustomer)
.WithOptionalDependent(k => k.VisitAddress)
.Map(x => x.MapKey("VisitAddressForCustomerId"))
.WillCascadeOnDelete(true);
Now if I have both 'WillCascadeOnDelete' set to true, it will work with an existing DB but not if I want to create the DB... I get the following exception message:
Introducing FOREIGN KEY constraint
'FK_dbo.Address_dbo.Address_Customer_VisitAddressForCustomerId' on table 'Address' may cause
cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON
UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
But this doesn't make sense to me since if I delete a deliveryAddress or a visitAddress, customer should stay untouched in this setting. So there should be no circular cascading.

The message says "may cause cycles or multiple cascade paths". It's the last part that matters. SQL server won't allow more than one FK constraint with cascaded deletes in one table.
By the way, note that cascaded delete does not prescribe what should happen if you delete an Address, but if you delete a Customer.
Also see this: SQL Server 2008 - Multiple Cascading FK's - Do i need a trigger?

Related

EF 6 Code First Class Generation Named and Mapped Incorrectly

I have an existing database with a table to which I have added 2 new columns.
Example table:
CREATE TABLE [dbo].[Tasks](
[TaskId] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NOT NULL,
[ResolutionId] [int] NULL,
[AmendedCategoryId] [int] NULL, -- New column
[AmendedResolutionId] [int] NULL, -- New column
)
These columns (each is an int) have a foreign key relationship which is the same as the 2 previously existing columns ("CategoryId" and "AmendedCategoryId" each FK to [TaskCategory] and "ResolutionId" and "AmendedResolutionId" each FK to [TaskResolution]).
When I use EF6 Code First to generate the C# data access classes for this, the results are confusing (to say the least).
Here is an example of what EF is producing:
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.TaskCategory1)
.WithOptional(e => e.TaskCategory2)
.HasForeignKey(e => e.ParentCategoryId);
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.Tasks)
.WithOptional(e => e.TaskCategory)
.HasForeignKey(e => e.AmendedCategoryId);
modelBuilder.Entity<TaskCategory>()
.HasMany(e => e.Tasks1)
.WithRequired(e => e.TaskCategory1)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
Resulting in the following:
The class property names for the new "Amended" columns (ex: "AmendedTaskCategory", "AmendedTaskResolution") do not match the
column names from the table, although the "AmendedTaskCategoryId"
and "AmendedTaskResolutionId" names DO match. Instead they have
"TaskCategory1" and "TaskResolution1" for their names.
The "modelBuilder.Entity" code that was generated is mapping the
"original" column to the new equivalent (ex: "TaskCategory" is being
mapped to "AmendedTaskCategory"), and the original generated
property is being left null (which is causing problems down-stream).
Why is EF generating the "1" and "2" classes &
properties?
How can I get Entity Framework 6 to "correctly" name and map the generated the classes and properties in the classes resulting from using the "Code First" EF functionality?

Error Deleting Child Entity in Entity Framework 6

We are using EF6 database first with AspNet Identity. AspNetUsers is our table of customers. It extends AspNet IdentityUser. Each customer has many devices. The abbreviated table structures are
CREATE TABLE [dbo].[AspNetUsers] (
[Id] NVARCHAR (128) NOT NULL
);
CREATE TABLE [dbo].[Devices] (
[DeviceID] INT IDENTITY (1, 1) NOT NULL,
[UserId] NVARCHAR (128) NOT NULL
);
ALTER TABLE [dbo].[Devices]
ADD CONSTRAINT [FK_Devices_AspNetUsers] FOREIGN KEY ([UserId]) REFERENCES [dbo].[AspNetUsers] ([Id]);
CustomersContext OnModelCreatingspecifies the relationship between AspNetUsers and Devices.
modelBuilder.Entity<ApplicationUser>()
.HasMany(e => e.Devices)
.WithRequired(e => e.User)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Device>()
.HasKey(e => e.DeviceId);
modelBuilder.Entity<Device>().Property(e => e.UserId)
.IsRequired()
.HasMaxLength(450);
The following code
var userDevices = user.Devices.ToList();
foreach (Device device in userDevices)
{
user.Devices.Remove(device);
}
await customersContext.SaveChangesAsync();
fails with the error
Cannot insert the value NULL into column 'UserId', table 'Customers.dbo.Devices'
That appears to be trying to remove the relationship without deleting the device. How do I delete a device using EF?
You want to delete the device. It is not possible to just remove it from the Devices list, as it cannot exist stand-alone by itself. It has a non-nullable foreign key (from column UserId) referencing AspNetUsers(Id) - deletion would break referential integrity.
To delete the device, add following statement inside foreach loop to successfully delete the devices associated with a user.
customersContext.Entry(device).State = EntityState.Deleted;
Another way would be to place following code inside foreach loop to delete the device directly from the device repository itself, instead of going through Devices navigation property available on ApplicationUser entity:
customersContext.Devices.Remove(device);
If you don't want to delete the records from the Devices table and just want to remove the relationship between AspNetUsers and all the Devices in one go, then you can assign null to the navigation property and save the changes. This will remove the relation between the entities without physically deleting the records from database.
user.Devices = null;

Entity Framework 6, cascade delete of mapped join table

I have simple "graph" representation stored in database. There is Node entity which has it's ID, Label and list of adjacent nodes.
public class Node
{
[Key]
public int Id { get; set; }
[StringLength(128)]
public string Label { get; set; }
public virtual ICollection<Node> AdjacentNodes { get; set; }
}
Then in OnModelCreating on database context class, I have:
modelBuilder.Entity<Node>()
.HasMany(n => n.AdjacentNodes)
.WithMany()
.Map(n => n.MapLeftKey("From").MapRightKey("To").ToTable("NodeEdge"));
Now, when I want to delete any node which has already any relation, I get constraint error:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.NodeEdge_dbo.Nodes_From". The conflict occurred in database "master", table "dbo.NodeEdge", column 'From'.
The statement has been terminated.
Join table looks like this:
CREATE TABLE [dbo].[NodeEdge] (
[From] INT NOT NULL,
[To] INT NOT NULL,
CONSTRAINT [PK_dbo.NodeEdge] PRIMARY KEY CLUSTERED ([From] ASC, [To] ASC),
CONSTRAINT [FK_dbo.NodeEdge_dbo.Nodes_From] FOREIGN KEY ([From]) REFERENCES [dbo].[Nodes] ([Id]),
CONSTRAINT [FK_dbo.NodeEdge_dbo.Nodes_To] FOREIGN KEY ([To]) REFERENCES [dbo].[Nodes] ([Id])
);
Is there any way how to add ON DELETE CASCADE on join table constraints - so it will clear up all references when I'm deleting nodes? (... and keeping model simple without having join table entity)
When the many-to-many association is a self reference, EF chooses to create the foreign keys as not cascading. If it would make both keys cascading there would be a circular cascade, which Sql Server doesn't allow. Apparently, EF doesn't want to choose for you which of both keys should be cascading, so it chooses none.
You can however add the ON DELETE CASCADE clause to the FK_dbo.NodeEdge_dbo.Nodes_From foreign key afterwards, or add it in the migration Up method.

Need proper syntax using EF6 Fluent API between 2 tables using PK and FK

If you all need more details I will send them for sure. Here is a screen shot containing 2 tables:
Basically this is what looks like in SQL today:
I am trying to use the fluent API for the relationship but not sure how to do it.
Table 1 has a PK and a FK.
Table 2 does not have a PK, only FK.
Below is an example of what I need but this code applies to a different set of tables. I am trying to get the "relationship" syntax correct for the scenario described here:
this.ToTable("Server");
//primary key
this.HasKey(t => t.serverId);
//properties
...
//relationships
this.HasMany(n => n.NetworkAdapters)
.WithRequired(s => s.Server)
.HasForeignKey(s => s.serverId)
.WillCascadeOnDelete(true);
Thank you
Without the primary key on table 2 you are most likely looking for a One-to–Zero-or-One relationship. You might possibly be requiring both ends which I will describe too.
One-to–Zero-or-One
// Configure the primary key for Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-zero or one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithOptional(t => t.Table2);
One-to-One
// Configure the primary key for the Table1
modelBuilder.Entity<Table1>()
.HasKey(t => t.networkAdapterId);
// Map one-to-one relationship
modelBuilder.Entity<Table2>()
.HasRequired(t => t.Table1)
.WithRequiredPrincipal(t => t.Table2);
See here for more details

Entity Framework CTP5 Code First - Possible to do entity splitting on a non-primary key?

Using EF CTP5, I am trying to do some entity splitting where the entity is constructed from two separate tables. Is it possible to do this splitting if the key on the two tables is not the primary key?
E.g. Id is my primary key on the Note entity. I want to get my CreatedUser details from a separate table but the primary key on this second table corresponds to CreatedUserId in the Note entity.
modelBuilder.Entity<Note>()
.Map(mc =>
{
mc.Properties(n => new
{
n.Id,
n.Title,
n.Detail,
n.CreatedUserId,
n.CreatedDateTime,
n.UpdatedUserId,
n.UpdatedDateTime,
n.Deleted,
n.SourceSystemId,
n.SourceSubSystemId
});
mc.ToTable("Notes");
})
.Map(mc =>
{
mc.Properties(n => new
{
n.CreatedUserId,
n.CreatedUser
});
mc.ToTable("vwUsers");
});
I've seen comments that entity splitting is only possible if the entity primary key exists in both tables?
Thanks in advance.
Yes, all the tables that are being generated in an entity splitting scenario must have the object identifier (e.g. Note.Id) as their primary key. You should consider creating a 1:* association between User and Note entities in this case.