1. Has method and With method
For example: Class A must contain an instance of Class B that is not null, and Class B can optionally contain an instance of Class A.
(a => ).WithOptional(b => );
1. Has method:
- HasOptional: The former (A) can contain an instance of the latter (B) or is null
- HasRequired: The former must contain an instance of the latter that is not null
- HasMany: The former contains a collection of the latter instances
2. With method:
- WithOptional: The latter (B) can contain an instance of the former (A) or null
- WithRequired: The latter must contain an instance of the former that is not null
- WithMany: The latter contains a collection of the former instances
2. One-to-one relationship:
The corresponding reference attributes must be configured first in both classes.
1. DataAnnotations data annotation method
Use ForeignKey foreign key annotation.
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public PersonPhoto Photo { get; set; } } public class PersonPhoto { [Key, ForeignKey("PhotoOf")] //Note: PersonId in PersonPhoto table is both a foreign key and a primary key public int PersonId { get; set; } public byte[] Photo { get; set; } public string Caption { get; set; } public Person PhotoOf { get; set; } }
2. Fluent API method
(1) 1:0..1 Relationship
PersonPhoto must belong to a Person, but Person does not necessarily have PersonPhoto. In this case, Person must exist, so it is the master-slave relationship with the master.
<Person>().HasOptional(t => ).WithRequired(t => ); <PersonPhoto>().HasRequired(p => ).WithOptional(p => );
(2) 1:1 Relationship
PersonPhoto must belong to a Person, and Person must also have PersonPhoto.
<Person>().HasRequired(p => ).WithRequiredPrincipal(); <PersonPhoto>().HasRequired(t => ).WithRequiredDependent(t => );
In this case, both must exist. To determine the master-slave relationship, you need to use WithRequiredPrincipal or WithRequiredDependent.
- If you choose WithOptionalPrincipal (the current entity is the principal; the target entity is the dependent object) There is a foreign key in the PersonPhoto table that points to the primary key of the Person table.
- If you choose WithOptionalDependen t, the opposite is true (the current entity is a dependent object; the target entity is the principal) means that there is a foreign key in the Person table, pointing to the primary key of the PersonPhoto table.
The Person table may not have corresponding PersonPhoto table data, but each PersonPhoto table must correspond to Person table data. It means that people can have no photos, but some photos must belong to someone.
3. One-to-many relationship:
1. DataAnnotations method
In many cases, we do not need to configure one-to-many relationships specifically. We detect the relationship between models through some reference attributes, navigation attributes, etc., and automatically generate foreign keys for us.
public class Destination {// Attractions public int DestinationId { get; set; } public string Name { get; set; } public string Country { get; set; } public string Description { get; set; } public byte[] Photo { get; set; } public List Lodgings { get; set; } } public class Lodging {// Accommodation public int LodgingId { get; set; } public string Name { get; set; } public string Owner { get; set; } public bool IsResort { get; set; } public decimal MilesFromNearestAirport { get; set; } public Destination Target { get; set; } }
Code First observes that there is a reference attribute to Destination in the Lodging class, or there is a collection navigation attribute Lodgings in the Destination, so it is inferred that the relationship between Destination and Lodging is a one-to-many relationship.
So in the generated database, the foreign key is generated for the automatic Lodging table: Foreign key name: Target_DestinationId
2. Change the nullable attribute of the foreign key and the name of the foreign key
By default, if your foreign key naming is specification, Code First will automatically set the property to a foreign key and no longer automatically create a foreign key.
Specification naming refers to the form that conforms to: name as follows: (The target type is Destination here)
- [Primary key name of the target type]: For example: DestinationId
- [Target type name]+[Target type primary key name]: For example: DestinationDestinationId
- [Navigation attribute name]+[Target type primary key name]: For example: TargetDestinationId
For example: The DestinationId attribute is automatically used as the primary key.
public class Lodging { public int? DestinationId { get; set; } public Destination Destination { get; set; } }
Of course, we can also add a foreign key to the class ourselves.
1. Use Data Annotations to specify a foreign key:
Note that the foreignKey position is different, and the parameters followed are also different.
[ForeignKey("Target")] public int TarDestinationId { get; set; } public Destination Target { get; set; }
or
public int TarDestinationId { get; set; } [ForeignKey("TarDestinationId")] public Destination Target { get; set; }
2. Use the Fluent API to specify foreign keys:
If the entity class does not define AccommodationId, you can use the Map method to directly specify the foreign key name: .Map(s => ("AccommodationId"))
(1) Lodging must belong to a Destination, and this relationship is 1:n.
<Destination>().HasMany(d => ).WithRequired(l => ).Map(l => ("DestinationId")); <Lodgings>().HasRequired(l => ).WithMany(d=>).HasForeignKey(l => );
(2) Post can exist alone and does not need to be attributed to the Blog. This relationship is 0..1:n.
<Destination>().HasMany(d => ).WithOptional(l => ).Map(l => ("DestinationId")); <Lodgings>().HasOptional(l => ).WithMany(d => ).HasForeignKey(l => );
3. Multiple references to the same entity
public class Person { public int PersonID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List PrimaryContactFor { get; set; } public List SecondaryContactFor { get; set; } } public class Lodging { public int LodgingId { get; set; } public string Name { get; set; } public string Owner { get; set; } public bool IsResort { get; set; } public decimal MilesFromNearestAirport { get; set; } public Destination Target { get; set; } //First contact person public Person PrimaryContact { get; set; } //Second contact public Person SecondaryContact { get; set; } }
Lodging (hotel) has two references to the Person table, namely PrimaryContact and SecondaryContact.
At the same time, there are also navigations for these two contacts in the Person table: PrimaryContactFor and SecondaryContactFor.
Because there are multiple one-to-many relationships between these two tables, Code First cannot handle this situation.
In order to let Code First know the correspondence between them, we need to use it hereReverse navigation propertiesLet's solve it.
(1) Use Data Annotations:
//First contact person[InverseProperty("PrimaryContactFor")] public Person PrimaryContact { get; set; } //Second contact[InverseProperty("SecondaryContactFor")] Person SecondaryContact { get; set; }
(2) Or use the Fluent API:
<Lodging>().HasOptional(l => ).WithMany(p => ); <Lodging>().HasOptional(l=>).WithMany(p=>)).Map(p => ("SecondaryPersonID "));;
Generate two foreign keys for the automatic Lodging table in the generated database: PrimaryContact _PersonID and SecondaryPersonID
4. Cascading deletion
1. If there is a one-to-many relationship between two tables, Code First will enable the cascading deletion function between two tables by default.
You can visually set not to cascading deletion in the database. When configuring this external key relationship in the Fluent API, you can set not to cascading deletion:
(d => ).WithRequired(l => ).Map(l => ("DestinationId")) //One to many and specify foreign key name.WillCascadeOnDelete(false); // Turn off cascade deletion
2. You can also remove this default convention in the OnModelCreating method in the context.
();
If you need to enable cascading deletion, you can use it in the FluentAPI relationship mapping. WillCascadeOnDelete(true) is enabled separately
4. Many-to-many relationship
If there are two classes, each with navigation attributes pointing to another class, Code First will consider that there is a many-to-many relationship between these two classes, for example:
public class Trip { public int TripId { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public decimal CostUSD { get; set; } public byte[] RowVersion { get; set; } public List Activities { get; set; } } public class Activity { public int ActivityId { get; set; } [Required, MaxLength(50)] public string Name { get; set; } public List Trips { get; set; } }
Code First generates an intermediate table ActivityTrips, which associates the primary keys of the other two tables as foreign keys to the intermediate table.
The naming of fields in the intermediate table is "[target type name]_[target type key name]".Activity_ActivityId and Trip_TripId
And also serves as a joint primary key for this new join table.
Specify the table name
If we want to specify the name and key name of the intermediate table, we can configure it using the Fluent API.
<Trap>().HasMany(t => ).WithMany(a => ).Map(m => { ("TripActivities"); ("TripIdentifier");//The primary key corresponding to the Trip ("ActivityId"); }); //or modelBuilder<Activity>.Entity().HasMany(a => ).WithMany(t => ).Map(m => { ("TripActivities"); ("ActivityId");//The primary key corresponding to the Activity ("TripIdentifier"); });
This is all about this article about Entity Framework configuration relationship. I hope it will be helpful to everyone's learning and I hope everyone will support me more.