Reputation: 526
[Table("Child", Schema = "dbo")]
public partial class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public int? Parent1Id { get; set; }
public int? Parent2Id { get; set; }
public virtual Parent Parent1 {get; set}
public virtual Parent Parent2 {get; set}
}
[Table("Parent", Schema = "dbo")]
public partial class Parent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public virtual Child Child1 { get; set; }
public virtual Child Child2 { get; set; }
//modelBuilder below would work if I had this
//public virtual ICollection<Child> Child1 { get; set; }
}
This is meant to be one-to-one relationship between Parent-Child. Parent1Id and Parent2Id are nullable.
I only found examples (with FluentAPI) for one-to-many relationship and if I had a collection of Child in Parent I'd have to do something like:
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child1)
.WithOptional(e => e.Parent1)
.HasForeignKey(e => e.Parent1Id);
modelBuilder.Entity<Parent>()
.HasMany(e => e.Child2)
.WithOptional(e => e.Parent2)
.HasForeignKey(e => e.Parent2Id);
Child will have 2 FK referencig Parent. Parent has no FK referencing child.
My question is, how can I do this as one-to-one relationship using EF 6.x? I saw the HasOne() method but that's from EFCore so I'm out of options in this one.
Thanks
Upvotes: 4
Views: 3599
Reputation: 205539
EF6 supports naturally only one-to-one relationships using the so called shared primary key associations, where the dependent PK is also used as FK. However they are not applicable in this scenario (when you need more than one one-to-one relationships between two entities). So you need one-to-one FK based relationships.
They are supported, but with the following restrictions:
HasForeignKey
fluent API when configuring one-to-one relationship). You have to remove them and work only with navigation properties. The FK column name can be specified using MapKey
inside Map
method.You have to accept these restrictions, otherwise you can't use EF6 with such model.
Start by removing the explicit FK properties from the model:
// remove these:
//public int? Parent1Id { get; set; }
//
//public int? Parent2Id { get; set; }
The next thing to consider is that EF relationships always consider one of the sides being principal, and the other being dependent. In one-to-many the "one" side is always the principal, and the "many" side is always the dependent. For one-to-one normally the required end is the principal, and optional end is the dependent, but when both ends are required or both end are optional (as in your case), you have to specify that by using the proper WithRequired
/ WithOptional
method, where Principal
/ Dependent
suffix specifies how to treat the entity being configured (the generic type argument to the Has
method). This is important and is usually confusing people, because the rest of the fluent configuration inside Map
applies always to the dependent entity, regardless of which entity is being used to start the configuration.
With that being said, in your sample there are two relationships, both with the Parent
being the principal, Child
being the dependent, and both ends being optional. Hence the fluent configuration could be
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child1)
.WithOptionalPrincipal(c => c.Parent1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Parent>()
.HasOptional(p => p.Child2)
.WithOptionalPrincipal(c => c.Parent2)
.Map(c => c.MapKey("Parent2Id"));
It could also be configured other way around:
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent1)
.WithOptionalDependent(p => p.Child1)
.Map(c => c.MapKey("Parent1Id"));
modelBuilder.Entity<Child>()
.HasOptional(c => c.Parent2)
.WithOptionalDependent(p => p.Child2)
.Map(c => c.MapKey("Parent2Id"));
Note: Use one or the another, not both. Usually the relationship configuration starts from the end having navigation property (since it is required for Has
methods), but when both end have navigations, then it's just a matter of personal preference/taste.
Upvotes: 3
Reputation: 16498
The parent is the principal and the child is the dependent, thus the child requires the parent but the child is optional to the parent:
modelBuilder.Entity<Child>()
.HasRequired(c => c.Parent1)
.WithOptional(p => p.Child1)
.HasForeignKey(c => c.Parent1Id);
Usually, you'd use the dependent's PK as the FK to the principal entity (which is what ensures the one-to-one relationship), but you're attempting something unusual here - you seem to want a one-to-two relationship. My opinion is to rethink your design - make it a 1:N and restrict number of dependents in application/DbContext code or derive two different types from Child
so that they are different entities in the DB/ERM model, then use the standard "PK as FK" one-to-one mapping.
Upvotes: 0
Reputation: 307
Note:One-to-one relationships are technically not possible in MS SQL Server. These will always be one-to-zero-or-one relationships. EF forms One-to-One relationships on entities not in the DB.
Upvotes: 3