What does principal end of an association means in 1:1 relationship in Entity framework
Asked Answered
K

3

277
public class Foo
{
    public string FooId{get;set;}
    public Boo Boo{get;set;}
}


public class Boo
{
    public string BooId{get;set;}
    public Foo Foo{get;set;}
}

I was trying to do this in Entity Framework when I got the error:

Unable to determine the principal end of an association between the types 'ConsoleApplication5.Boo' and 'ConsoleApplication5.Foo'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

I have seen questions on StackOverflow with a solution for this error, but I want to understand what the term "principal end" means.

Kanchenjunga answered 30/6, 2011 at 7:57 Comment(1)
See learn.microsoft.com/en-us/ef/core/modeling/relationships for an explanation of the termsDespondent
M
384

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.

In case of entity framework FK in dependent must also be its PK so in your case you should use:

public class Boo
{
    [Key, ForeignKey("Foo")]
    public string BooId{get;set;}
    public Foo Foo{get;set;}
}

Or fluent mapping

modelBuilder.Entity<Foo>()
            .HasOptional(f => f.Boo)
            .WithRequired(s => s.Foo);
Microreader answered 30/6, 2011 at 9:0 Comment(13)
thanks, now i also understood the "foreign key vs independent association" article on your blog :)Kanchenjunga
@Ladislav, I need to make two independant tables that both have an optional reference to each other (one to one), I want them both to have their own PKs each, how is this possible? I posted a separate question.Dick
You have no idea how many hours it took to find an answer to this - ms documentation is POOOOOOP ty.Cutting
Note you may need to add using System.ComponentModel.DataAnnotations.Schema; to get ForeignKey in VS2012Emphasis
Does that mean that Foo is the principal then?Confiture
@Confiture you are correct Boo is the dependent, requires a Foo, and gets the foreign key. Foo is the principal and can exist without a Boo.Rubella
Foreign key vs. Independent associations in EF 4Superlative
@Ladislav 'In case of entity framework FK in dependent must also be its PK' does this argument still stand in EF 6 ?Hyacinthe
@SirajMansour: Yes it does.Microreader
@LadislavMrnka so my only option is using the 'Required' attribute in case the FK is not the pk in the depenedant ?Hyacinthe
Worth mentioning that both keys can be required in a 1-1 relationship but the notion of Principal and Dependent entity is still valid, in which case you would choose the following Fluent API option: modelBuilder.Entity<Foo>().HasRequired(f => f.Boo).WithRequiredPrincipal(s => s.Foo);Understrapper
@LadislavMrnka What if you have the same scenario but need the FK in a different field which is not the PK ? How would you do that ?Exeter
I have a similar question as earlier: does the requirement of the dependent FK being the same as the PK still hold true for EF Core? Based on the example of one-to-one mappings in learn.microsoft.com/en-us/ef/core/modeling/relationships (see toward the bottom with Blog and BlogImage), it appears the dependent maintains a seperate FK from it's PK. It doesn't mention it explicitly, but there's no attributes or mapping indicating FK and PK are both the same.Cerellia
M
187

You can also use the [Required] data annotation attribute to solve this:

public class Foo
{
    public string FooId { get; set; }

    public Boo Boo { get; set; }
}

public class Boo
{
    public string BooId { get; set; }

    [Required]
    public Foo Foo {get; set; }
}

Foo is required for Boo.

Mcpeak answered 21/9, 2012 at 2:13 Comment(6)
This was correct for my following code where I wanted a map between the two as a separate entity public class Organisation { public int Id { get; set; }} public class user { public int Id { get; set; }} public class UserGroup { [Key] public int Id { get; set; } [Required] public virtual Organisation Organisation { get; set; } [Required] public virtual User User { get; set; } }Bastardize
I'm using Oracle and none of the fluent api's worked for me. Thanks bro. So simple.Reductase
Be aware that when using this solution you will get validation exceptions when you try and update a Boo that you just retrieved from the database, unless you first trigger the lazy-load of the Foo property. entityframework.codeplex.com/SourceControl/network/forks/…Goree
shouldn't Boo Boo be virtual then?Solemnize
@Goree the link is bad now, how do you make that change?Palfrey
Configuring via FluentAPI wasn't working, still not sure why - but adding Required to the primary's property in the dependent class worked.Mcgannon
W
9

This is with reference to @Ladislav Mrnka's answer on using fluent api for configuring one-to-one relationship.

Had a situation where having FK of dependent must be it's PK was not feasible.

E.g., Foo already has one-to-many relationship with Bar.

public class Foo {
   public Guid FooId;
   public virtual ICollection<> Bars; 
}
public class Bar {
   //PK
   public Guid BarId;
   //FK to Foo
   public Guid FooId;
   public virtual Foo Foo;
}

Now, we had to add another one-to-one relationship between Foo and Bar.

public class Foo {
   public Guid FooId;
   public Guid PrimaryBarId;// needs to be removed(from entity),as we specify it in fluent api
   public virtual Bar PrimaryBar;
   public virtual ICollection<> Bars;
}
public class Bar {
   public Guid BarId;
   public Guid FooId;
   public virtual Foo PrimaryBarOfFoo;
   public virtual Foo Foo;
}

Here is how to specify one-to-one relationship using fluent api:

modelBuilder.Entity<Bar>()
            .HasOptional(p => p.PrimaryBarOfFoo)
            .WithOptionalPrincipal(o => o.PrimaryBar)
            .Map(x => x.MapKey("PrimaryBarId"));

Note that while adding PrimaryBarId needs to be removed, as we specifying it through fluent api.

Also note that method name [WithOptionalPrincipal()][1] is kind of ironic. In this case, Principal is Bar. WithOptionalDependent() description on msdn makes it more clear.

Wheeling answered 14/7, 2016 at 6:39 Comment(3)
What if you actually want the PrimaryBarId property? This is ridiculous to me. If I add the property and say it's the foreign key, I get an error. But if I don't have the property, then EF will create it anyways. What's the difference?Heffron
@ChrisPratt This might not sound reasonable. I arrived at this solutions after trail and error. Was not able to configure one-to-one mapping when I had PrimayBarId property in Foo entity. Likely the same solution that you tried. Limitations in EF perhaps?Wheeling
Yeah, it is. I've come to find out that EF to this day has never implemented unique indexes. As a result, the only way available to map a one-to-one is to use the primary key of the principal end as the primary key of the dependent end, because a primary key is by nature unique. In other words, they half-implemented it and took a shortcut that dictates that your tables have to be design in a non-standard way.Heffron

© 2022 - 2024 — McMap. All rights reserved.