When you create a one-to-many relationship between two entities in Code First Entity Framework model, you need to set “links” in both entities. In my case they are Client and Phone entities.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Client { [Key] public int Id { get; set; } public string Name { get; set; } public ICollection Phones { get; set; } } public class Phone { [Key] public int Id { get; set; } public string Number { get; set; } public virtual Client Client { get; set; } } |
During the migration EF will create a field Client_Id (entity name + field name of the primary key) and a foreign key.
I don’t like auto-generated names for table fields and I want to get a precise control over it. For example, I want to give another name for a foreign key field, say, ClientId without underscore. EF allows to do this with a couple of lines of code. You need to add a ClientId property and a ForeignKey attribute pointing to ClientId.
1 2 3 4 5 6 7 8 9 10 11 |
public class Phone { [Key] public int Id { get; set; } public string Number { get; set; } public int ClientId { get; set; } [ForeignKey("ClientId")] public virtual Client Client { get; set; } } |
To be frankly, that’s my favorite way to declare a relationship.
Another case is when you need to create two or more relationships between the same two entities. At this time you must set an InverseProperty attribute in Client class, or you’ll get 4 fields and foreign keys instead of 2 required.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Client { [Key] public int Id { get; set; } public string Name { get; set; } [InverseProperty("Client")] public ICollection Phones { get; set; } [InverseProperty("SecondClient")] public ICollection SecondPhones { get; set; } } public class Phone { [Key] public int Id { get; set; } public string Number { get; set; } public virtual Client Client { get; set; } public virtual Client SecondClient { get; set; } } |
Now we get the following structure. Please pay attention that auto-generated fields Client_Id and SecondClient_Id are null.
When I placed ForeignKey attributes in Phone class to replace the auto-generated names with my own, I’ve got an error on Update-Database phase.
Introducing FOREIGN KEY constraint 'FK_dbo.Phones_dbo.Clients_OwnerClientId' on table 'Phones' 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.
That’s because EF tries to make the FK fields not null and use a cascade delete convention. Open the database migration .cs file and look at these lines:
1 2 3 4 5 |
AlterColumn("dbo.Phones", "ClientId", c => c.Int(nullable: false)); AlterColumn("dbo.Phones", "OwnerClientId", c => c.Int(nullable: false)); ... AddForeignKey("dbo.Phones", "ClientId", "dbo.Clients", "Id", cascadeDelete: true); AddForeignKey("dbo.Phones", "OwnerClientId", "dbo.Clients", "Id", cascadeDelete: true); |
If you change the declaration of FK fields to nullable type (i.e. int?), everything will be OK. The declaration of foreign key in Phone class
1 2 3 |
public int? ClientId { get; set; } [ForeignKey("ClientId")] public virtual Client Client { get; set; } |
and the resulting database structure:
The other option is to change manually the database migration .cs file and set cascadeDelete to false.
One Reply to “Code First. Creating Relationships Between Entities”