.Net Entity Framework Introducing FOREIGN KEY constraint exception - c#

Here is a piece of my DB, creaated according to code first principle:
I have an abstract class Client (a table of this class is created in my data base):
public abstract class Client
{
public int ClientId { get; set; }
[Required(ErrorMessage = "Name is required for Client")]
public string ClientName { get; set; } // name
[Required(ErrorMessage = "Phone number is required for Client")]
public string ClientPhoneNumber { get; set; } // phone number
public string Email { get; set; } // email
public string Comment { get; set; } // note
[Required(ErrorMessage = "Client should be enabled or disabled")]
public bool IsDisabled { get; set; }
}
Three classes are inherited from it:
1)
public class PrivatePerson : Client // дядя Вася
{
public string PrivatePersonSurname { get; set; }
}
2)
public class Firm : Client
{
[Required(ErrorMessage = "Ownership is required for Firm")]
public virtual Ownership Ownership { get; set; }
[Required(ErrorMessage = "Client address is required for Firm")]
public virtual ClientAddress FirmAddress { get; set; }
}
3)
public class AdvertisingAgency : Client
{
[Required(ErrorMessage = "Ownership is required for Advertising agency")]
public virtual Ownership Ownership { get; set; }
[Required(ErrorMessage = "Client address is required for Advertising agency")]
public virtual ClientAddress AdvertisingAgencyAddress { get; set; }
}
!! Firm and AdvertisingAgency have similar fields
And here is ClientAddress class:
public class ClientAddress
{
public int ClientAddressId { get; set; }
[Required(ErrorMessage = "Postal code is required for Client Address")]
public int PostalCode { get; set; }
[Required(ErrorMessage = "City is required for Client Address")]
public virtual City ClientCity { get; set; }
public int POBox { get; set; }
public virtual Street ClientStreet { get; set; }
public string StreetNumber { get; set; }
public int Appartment { get; set; }
public string ClientAddressComment { get; set; }
}
Context:
public virtual DbSet<Client> Clients { get; set; }
public virtual DbSet<ClientAddress> ClientAddresses { get; set; }
Well, when i'm trying to create a DB, I get this exception:
System.Data.SqlClient.SqlException
HResult=0x80131904
Message=Introducing FOREIGN KEY constraint 'FK_dbo.Clients_dbo.ClientAddresses_FirmAddress_ClientAddressId' on table 'Clients' 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.
Can anyone, please, point out my mistake?
Thanks
This is my context (partly) :
public class FivePlusDBContext : DbContext
{
public FivePlusDBContext() : base("name = FivePlus")
{
}
public virtual DbSet<City> Cities { get; set; }
public virtual DbSet<Street> Streets { get; set; }
public virtual DbSet<Client> Clients { get; set; }
public virtual DbSet<ClientAddress> ClientAddresses { get; set; }
public virtual DbSet<Ownership> Ownerships { get; set; }
}
To create a record, i do the following:
using (var ctx = new FivePlusDBContext())
{
City C_1 = new City() { CityName = "Name" };
ctx.Cities.Add(C_1);
ctx.SaveChanges();
}

First of all be aware that EntityFramework by default creates just one table per hierarchy (see here for details). In that case a special column (Discriminator) gets added to the Clients table to distinguish between persistent classes.
Now to your question.
According your model I assume that you want to use the same ClientAddress for multiple clients of type Firm and AdvertisingAgency. As both classes Firm and AdvertisingAgency have a property of type ClientAddress EntityFramework will generate two foreign keys on Clients table that point to ClientAddress (FK_dbo.Clients_dbo.ClientAddresses_AdvertisingAgencyAddress_ClientAddressId and FK_dbo.Clients_dbo.ClientAddresses_FirmAddress_ClientAddressId). For both foreign keys EntityFramework enables cascading delete by default. This leads to the exception that this may cause multiple cascade paths.
There are multiple possibilities that could solve the problem.
Create two different address types and refer to one of them in Firm and to the other one in AdvertisingAgency class (reusing addresses will not be possible anymore)
Change strategy to Table per Concrete class (TPC) (see here could maybe also solve the problem (not tested)
Disable cascading delete using Fluent API
To disable cascading delete add the following code to your Context class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Firm>()
.HasRequired(c => c.FirmAddress)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<AdvertisingAgency>()
.HasRequired(c => c.AdvertisingAgencyAddress)
.WithMany()
.WillCascadeOnDelete(false);
}
Mention: this could lead to orphaned addresses. When deleting a client its address will not get deleted. However the address could still be related to another client. Furthermore deletion of an address will not be possible as long as the address is related to at least one client. If trying to delete an address that still relates to multiple clients the following exception will be thrown.
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

Related

Entity Framework Code First One to One Cascade Deletes

Have and "Address" model used by several other models ("Employee" & "Client").
I would call this a one to one relationship, I could be wrong. The address is required by both of the other models. Remove, deletes only the parent object.
Tried in both EF Core and EF6. Remove deletes the parent object, but not the "Address" object.
public class Address
{
public int AddressID { get; set; }
public string Street { get; set; }
public string CityStateZip { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Address EmployeeAddress { get; set; }
}
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Address ClientAddress { get; set; }
}
No error messages - context.remove simply won't delete the child object. Complete noob here when it comes to EF. Sorry, this is probably a very basic question, but please believe that I have searched extensively. Most solutions suggest a foreign key back to the parent - but, in this case, the child object can be used (but not shared) in several different models.
The same Address object can be used in multiple Employee and/or Client instances as currently implemented.
The suggestion you received
Most solutions suggest a foreign key back to the parent - but, in this case, the child object can be used (but not shared) in several different models.
informs Entity Framework that a given Address can only appear in one specific Employee/Client.
You should be able to resolve this by having Employee and Client inherit from a common base class, e.g.
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[Required]
public virtual Address PersonAddress { get; set; }
}
public class Employee : Person
{
// Other properties that make Employee unique go here
}
public class Client : Person
{
// Other properties that make Client unique go here
}
Then add the backreference to the base class
public class Address
{
public virtual int AddressID { get; set; }
public virtual string Street { get; set; }
public virtual string CityStateZip { get; set; }
[Required]
public virtual Person AddressOf { get; set; }
}

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.

Code First Optional One-To-One Relationship

Writing a model for situation where I have two tables which are customers and users. Each user record might have an optional related customer record and vice versa, but none of them is a must. I figured out that FK Associations are not what I need, but Independent Associations are. But I just can find a way to make it work, I keep getting the 'Unable to determine the principal end...The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.' exception.
My models are very simple:
public class User
{
[Key]
public int Id { get; set; }
[StringLength(20)]
public string CustomerId { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
//[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
//[InverseProperty("Customer")]
public virtual User User { get; set; }
}
I've tried to add the ForeignKeyAttribute and InversePropertyAttribute, which are currently commented out, but they didn't help either. I would prefer to use data annotations and not fluent API, if it's possible in my case.
In one-to-one relation one end must be principal and second end must be dependent. Principal end is the one which will be inserted first and which can exist without the dependent one. Dependent end is the one which must be inserted after the principal because it has foreign key to the principal. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key.This problem is most easily solved by using a ForeignKey annotation on the dependent class to identify that it contains the foreign key. In your case, Customer could be the dependent and its key, Customer.UserId, should also be the foreign key. But both Keys must be declared using the same type:
public class User
{
[Key]
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual User User{ get; set; }
}
I don't know how to resolve your problem using Data Annotations, but if you want to use Fluent Api, I think the configuration of the relationship would be like this:
modelBuilder.Entity<User>().HasOptional(u => u.Customer).WithOptionalPrincipal(c => c.User);
Update
I understand your escenario, but if you have the same columns that you show in your model, I think you should have a one-to-many relationship mapped in DB instead one-to-one. Try to map your relationship this way:
public class User
{
[Key]
public int Id { get; set; }
public string Password { get; set; }
public bool Locked { get; set; }
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
public class Customer
{
[Key]
[Column("Id", TypeName = "nvarchar")]
[StringLength(20)]
public string Id { get; set; } // nvarchar 20
[Required]
public string GivenName { get; set; } // nvarchar 100
[Required]
public string Surname { get; set; } // nvarchar 100
public virtual ICollection<User> Users { get; set; }
}
Remember map your properties with the same column'names that you have in DB.

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.

Relationship troubles with Entity Framework

I need help creating the relationship in entity framework as everything I have tried gives me errors when trying to add the migration or if I get passed that then I try to update the database and get an error about indexes with the same name.
public class Profile
{
public Profile()
{
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string VersionCreated { get; set; }
public string DiskLocation { get; set; }
public string Name { get; set; }
public DateTime DateTime { get; set; }
public virtual Product Product { get; set; }
public virtual Instance OriginalInstance { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
public class Instance
{
public Instance()
{
TestResults = new HashSet<TestResult>();
Environments = new HashSet<Environment>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string UserFriendlyName { get; set; }
public virtual Product Product { get; set; }
public virtual Profile LastKnownProfile { get; set; }
public virtual Computer Computer { get; set; }
public virtual ICollection<TestResult> TestResults { get; set; }
public virtual ICollection<Environment> Environments { get; set; }
}
The problem with the above classes is that the OrginalInstance property on the Profile class and the LastKnownProfile in the Instance class are supposed to just be foreign keys to those specific tables and they probably won't be the same very often. They can also both possibly be null.
I have tried:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile);
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance);
This gave me an Unable to determine the principal end of an association between the types 'EcuWeb.Data.Entities.Instance' and 'EcuWeb.Data.Entities.Profile'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations. error.
and with:
modelBuilder.Entity<Instance>().HasRequired(i => i.LastKnownProfile).WithOptional();
modelBuilder.Entity<Profile>().HasRequired(p => p.OriginalInstance).WithOptional();
The database adds a foreign key reference back to itself.
...that the OrginalInstance property on the Profile class and the
LastKnownProfile in the Instance class are supposed to just be foreign
keys to those specific tables and they probably won't be the same very
often. They can also both possibly be null.
In this case you actually want two one-to-many relationships between Profile and Instance if I don't misunderstand your quote above. It would mean that many Profiles can have the same OriginalInstance and that many Instances can have the same LastKnownProfile. The correct mapping would look like this then:
modelBuilder.Entity<Profile>()
.HasOptional(p => p.OriginalInstance)
.WithMany()
.Map(m => m.MapKey("OriginalInstanceId"));
modelBuilder.Entity<Instance>()
.HasOptional(i => i.LastKnownProfile)
.WithMany()
.Map(m => m.MapKey("LastKnownProfileId"));
The lines with MapKey are optional. Without them EF will create a foreign key with a default name.
Also note that you must use HasOptional (instead of HasRequired) if "both can possibly be null".

Categories