Configuring 1:N relationship delete cascade fails in Code First - c#

I'm having troubles getting this bit of a Code First model to produce a database.
Here are the two models in question:
public class Workflow : BaseEntity, IBaseEntity
{
public string Name { get; set; }
public string Description { get; set; }
public bool Published { get; set; }
public DateTime? PublishedOn { get; set; }
public int Revision { get; set; } = 0;
public Guid PermanentId { get; set; }
public virtual ICollection<WorkflowStage> WorkflowStages { get; set; }
public virtual ICollection<WorkflowDataFactor> WorkflowDataFactors { get; set; }
public virtual ICollection<WorkflowInstance> WorkflowInstances { get; set; }
}
And here's the 2nd model:
public class WorkflowStage : BaseEntity, IBaseEntity
{
public int WorkflowId { get; set; }
public virtual Workflow Workflow { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Color { get; set; }
public int Order { get; set; }
public int Revision { get; set; } = 0;
public Guid PermanentId { get; set; }
public virtual ICollection<WorkflowStageStep> WorkflowStageSteps { get; set; }
public virtual ICollection<WorkflowStageInstance> WorkflowStageInstances { get; set; }
}
BaseEntity provides all my other entities with an Id property, which is the primary key.
After getting this error:
Introducing FOREIGN KEY constraint
'FK_dbo.WorkflowStage_dbo.Workflow_WorkflowId' on table
'WorkflowStage' 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.
I implemented this in my context class:
modelBuilder.Entity<Workflow>().HasMany(x => x.WorkflowStages).WithRequired().WillCascadeOnDelete(false);
If I understand correctly, this is saying if a WorkflowStage gets deleted, the associated Workflow shouldn't be deleted.
Isn't it what EF6 is looking for according to the error at the top ?
Thanks.

Change the code in your context class for:
modelBuilder.Entity<Workflow>()
.HasOptional<WorkflowStages>(s => s.WorkflowStages)
.WithMany()
.WillCascadeOnDelete(false);

"Introducing FOREIGN KEY constraint 'FK_dbo.WorkflowStage_dbo.Workflow_WorkflowId' on table 'WorkflowStage' may cause cycles or multiple cascade paths..."
What the could mean is that you have multiple tables that lead to another table and each of them has the OnCascade setting. You can't do this in SQL Server.
Table1 ==> Table2 with Cascade
Table3 ==> Table2 with Cascade <<-- no no no!!
So how many tables interact with WorkFlowStage? Are there multiple tables that could cascade to it? Would it be possible to wind up with orphan records by removing the cascade from one table or the other? For instance if you remove the cascade delete from WorkFlow then could you wind up with WorkFlowStage that doesn't belong to anything? Perhaps removing the cascade from the other table involved would be more appropriate.
In some cases I have created Triggers for AFTER DELETE on a table which can be used to clean up child records without the constraint.
HTH,
Mike

Related

Would the Entity Framework's navigational properties work if I drop foreign key constraints from the database?

As you know that developers mostly mock the relationship between tables instead of using physical relationships between table (yeah, the line drawn from one table to another if you put a foreign key constraint on the column).
But I believe that Entity Framework doesn't work properly if physical relationships aren't there for navigational properties.
So, is there any way around?
My classes:
public class Phones
{
public int ID { get; set; }
public string Model { get; set; }
public string Manufacturer { get; set; }
public List<Users> Users { get; set; }
}
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
}
This creates a 1-M relationship from User -> Sims.
But what if I drop the foreign key constraint and leave it as it is, how will the navigational properties work then?
At this case better to remove references from both classes and handle relations manually outside of these classes:
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
//public User User { get; set; }
public int UserID { get; set; }
}

Foreign Key creation in EF Core code first

I have 2 models:
public class GaOrgOrders
{
public virtual Guid Id { get; set; }
[ForeignKey("GaOrganizations")]
public virtual Guid OrgId { get; set; }
[ForeignKey("GaApps")]
public virtual Guid AppId { get; set; }
[ForeignKey("GaOrgUserOrganizations")]
public virtual Guid OrgUserId { get; set; }
[ForeignKey("GaServicesTariffs")]
public virtual Guid ServiceTariffId { get; set; }
public virtual bool IsTemporary { get; set; }
public virtual Apps GaApps { get; set; }
public virtual Organizations GaOrganizations { get; set; }
public virtual OrgUserOrganizations GaOrgUserOrganizations { get; set; }
public virtual GaServicesTariffs GaServicesTariffs { get; set; }
}
and
public class GaOrganizations
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsDeleted { get; set; }
}
When I try to update database and add these tables I get an error:
Introducing FOREIGN KEY constraint 'FK_GaOrgOrders_GaOrganizations_OrgId' on table 'GaOrgOrders' 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.
If I remove "ForeignKey" attribute from OrgId property - error disappears, but it creates in database a second field(GaOrganizationsId) and makes it a FK. Other Keys work fine. What's wrong with OrgId?
EF doesn't understand the relationship between the two entities so you might need to clarify using fluent API in your "OnModelCreating" method.
Using something like
modelBuilder.Entity<ClassB>().HasRequired(x => x.ClassA).WithOptional(x => x.ClassB);
where ClassA and ClassB are my entities having a 1 to 0 or 1 relationship between them.

Introducing FOREIGN KEY constraint key on table table may cause cycles or multiple cascade paths. Specify ON DELETE ... Error

i was trying to run the Update-Database command in Nugget Package Manager console but wasnt successful as i kept getting the error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' 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. See previous errors..
I want to set up relationship in which there is a class called Team.cs that contains the below properties
public class Team
{
public int TeamId { get; set; }
public string TeamName { get; set; }
public Decimal MonthlyTarget { get; set; }
public ICollection<SalesAgent> Agents { get; set; }
}
which means a team has many Agents and there is another class called SalesAgent.cs which contain info about agents
public class SalesAgent
{
[Key]
public int AgentId { get; set; }
public string AgentFirstName { get; set; }
public string AgentLastName { get; set; }
public string HomeAddress { get; set; }
public bool IsActive { get; set; }
public string AgentPhone { get; set; }
public Decimal MonthlyTarget { get; set; }
public int TeamId { get; set; }
public virtual Team Team { get; set; }
}
Now i want a class which i would be able add the relationship between a team and an agent i.e in essence i want to be able to assign a team leader to each team so i set up the class below
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
public int AgentId { get; set; }
public virtual Team Team { get; set; }
[ForeignKey("AgentId")]
public virtual SalesAgent Agent { get; set; }
}
Upon running "Update-Database Command" I get an error that The ForeignKeyAttribute on property 'AgentId' on type 'SalesForce.Models.TeamToLeader' is not valid. The navigation property 'SalesAgent' was not found on the dependent type 'SalesForce.Models.TeamToLeader'. The Name value should be a valid navigation property name.
So i changed the model to
public class TeamToLeader
{
[Key]
public int TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
and that resulted in this error
Introducing FOREIGN KEY constraint 'FK_dbo.TeamToLeaders_dbo.Teams_TeamId' on table 'TeamToLeaders' 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. See previous errors.
Help please.
You should diasble OneToManyCascadeDeleteConvention to force EF not to use cascade delete. In DbContext add:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
...
Or you can make foreign keys nullable:
public class TeamToLeader
{
[Key]
public int? TeamToLeaderId { get; set; }
[ForeignKey("Team")]
public int? TeamId { get; set; }
[ForeignKey("SalesAgent")]
public int AgentId { get; set; }
public virtual Team Team { get; set; }
public virtual SalesAgent Agent { get; set; }
}
Depends which behavior you prefer.
You can also use fluent API:
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TeamToLeader>().HasRequired(i => i.Agent).WithMany().WillCascadeOnDelete(false);
}
...
Note that your model Team has many SalesAgent and many TeamToLeader.
There should be TeamToLeaders collection in your Team and SalesAgent model :
...
public virtual ICollection<TeamToLeader> TeamToLeaders { get; set; }
...
I'm not sure if you need Team to many SalesAgent relation anymore.
As this link, and this link saids...
It is theoretically correct but SQL server (not Entity framework) doesn't like it because your model allows single employee to be a member of both First and Second team. If the Team is deleted this will cause multiple delete paths to the same Employee entity.
SQL server doesn't allow multiple delete paths to the same entity.
This link said that it can be solved by disabling OneToManyCascadeDeleteConvention and ManyToManyCascadeDeleteConvention, but those deleting operations SHOULD BE done by codes manually.

double relationship between entities in EntityFramework 6

My problem looks simple. I need to implement a relationships between items in the database. For example: relationship between entities like computer and software shows users that computer stores a specific software and similarly - a software is installed in the specific computer. I think I should implement an entity with source id and target id or something similar. I wrote some code using code first in EntityFramework 6. Here are two classes:
public class ConfigurationItem
{
public int Id { get; set; }
public String Name { get; set; }
public String DeploymentState { get; set; }
public String IncidentState { get; set; }
[DataType(DataType.MultilineText)]
public String Description { get; set; }
[DataType(DataType.MultilineText)]
public String Note { get; set; }
public virtual ICollection<Relationship> Relationship { get; set; }
}
public class Relationship
{
[Key]
public int RelationshipId { get; set; }
[ForeignKey("ConfigurationItem")]
public int SourceId { get; set; }
[ForeignKey("ConfigurationItem")]
public int TargetId { get; set; }
public String Type { get; set; }
public virtual ConfigurationItem Source { get; set; }
public virtual ConfigurationItem Target { get; set; }
}
This solution doesn't work. I need a tip or something what should I try to make it work properly. EF throws an error about foreign key:
The ForeignKeyAttribute on property 'SourceId' on type 'cms_1.Models.Relationship' is not valid. The navigation property 'ConfigurationItem' was not found on the dependent type 'cms_1.Models.Relationship'. The Name value should be a valid navigation property name.
When I try to resolve it EF throws an error about cascade deleting. I know how to disable it but I just don't want to. I need a proper solution with that feature but I think I don't know how to do a model representing given scenario.
Simply - I need to store two foreign keys from entity "A" in the entity "B". How is it possible?
from a quick review , I can tell that you need 3 tables :
first : Computer
second : Software
third : a table , lets call it ComputerSoftware which tell which software has in what computer ( or you can also see it - which computer use what software ), which has ComputerID column and SoftwareID column.
example (source)
class Country
{
public int Id { get; set; }
public virtual ICollection<CountryCurrency> CountryCurrencies { get; set; }
}
class Currency
{
public int Id { get; set; }
}
class CountryCurrency
{
[Key, Column(Order=0)]
public virtual int CountryId { get; set; }
[Key, Column(Order=1)]
public virtual int CurrencyId { get; set; }
public virtual Country Country { get; set; }
public virtual Currency Currency { get; set; }
}
Your issue could be that in the migration file creating those tables, it will have something like
.ForeignKey("dbo.Relationship", t => t.Id, cascadeDelete: true)
This will be set on both tables, ConfigurationItem and Relationship of their Primary Key fields. When you delete one, that config tells SQL Server to delete the relationships as well and the relationship probably has a cascadeDelete: true to the parent. This will cause the cyclical cascading delete issue you are experiencing.
After the migration has been generated, go in and change one or all to cascadeDelete: false and this will fix that issue. This is what EF generates by default if I recall.

Entity Framework Cascade Delete with Multiple Column Key

I have these models set up:
public class Advisor
{
public virtual int AdvisorId { get; set; }
public virtual int UserId { get; set; }
public User User { get; set; }
public ICollection<AdvisorStudentMap> AdvisorStudentMaps { get; set; }
}
public class AdvisorStudentMap
{
[Required]
public virtual int AdvisorId { get; set; }
[Required]
public virtual int UserId { get; set; }
}
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual string FirstName { get; set; }
public ICollection<AdvisorStudentMap> AdvisorStudentMaps { get; set; }
}
In my OnModelCreating I have:
modelBuilder.Entity<AdvisorStudentMap>()
.HasKey(t => new {t.AdvisorId, t.UserId});
In my fluent api how do I set it up so that when I delete an advisor, it deletes the AdvisorStudentMap as well? I keep getting the error: Message=Introducing FOREIGN KEY constraint 'FK_dbo.AdvisorStudentMaps_dbo.Users_UserId' on table 'AdvisorStudentMaps' 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. See previous errors.
Update - also in OnModelCreating I have:
modelBuilder.Entity<Advisor>()
.HasRequired(t => t.AdvisorStudentMaps).WithMany().WillCascadeOnDelete(true);
With that I get the error 'Cascading foreign key 'FK_dbo.Advisors_dbo.AdvisorStudentMaps_AdvisorId_UserId' cannot be created where the referencing column 'Advisors.AdvisorId' is an identity column.
You appear to be trying to model a many-to-many relationship between Students and Advisors. This would be the way you'd normally do that:
public class Advisor
{
//Key fields don't need to be marked virtual
public int AdvisorId { get; set; }
//If you want the property to lazy load then you should mark it virtual
public virtual ICollection<Student> Students{ get; set; }
//Advisors have a UserProfile
public int UserProfileId{get;set;}
public virtual UserProfile UserProfile {get; set;}
}
public class Student
{
public int StudentId { get; set; }
public virtual ICollection<Advisor> Advisors { get; set; }
//Students also have a UserProfile
public int UserProfileId{get;set;}
public virtual UserProfile UserProfile {get; set;}
}
public class UserProfile
{
public int UserProfileId{get;set;}
//NB not marked virtual - that is only needed on navigation properties when
//we want to use lazy loading
public string UserName {get;set}
public string FirstName {get;set}
}
Entity framework will automatically create the join table to model the relationship. You don't need the AdvisorStudentMap entity unless you need to add attributes to the relationship.
As for the cascade on delete problem. If you delete a User then this can cascade to the Student table and the Advisor table. There is a cascade path from Student to StudentAdvisorMap and another from Advisor to StudentAdvisorMap. Hence multiple cascade paths. Sql Server does not allow this. You will have to explicitly implement the deletes in your code to avoid this
I figured this out with help from this link: http://blog.cdeutsch.com/2011/09/entity-framework-code-first-error-could.html
As he says on the blog, it isn't very intuitive but here is the syntax that made it work:
modelBuilder.Entity<AdvisorStudentMap>()
.HasRequired(u=>u.User)
.WithMany(m=>m.AdvisorStudentMaps)
.WillCascadeOnDelete(false);

Categories