8

In EntityFramework code first models, there exists a 1:1 relationship:

public class Child1
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string Name { get; set; }

    public Child2 Child2 { get; set; }
}

public class Child2
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [ForeignKey("Child1")]
    public int Id { get; set; }

    public string Name { get; set; }

    public Child1 Child1 { get; set; }
}

When I tried to insert some data to the database, it thrown an exception:

{"A dependent property in a ReferentialConstraint is mapped to a 
  store-generated column. Column: 'Id'."}

It seems I cannot use auto generated Id for Child2, how can I keep this feature and make the relationship established successfully meanwhile?

JotaBe
  • 38,030
  • 8
  • 98
  • 117
Jerry Bian
  • 3,998
  • 6
  • 29
  • 54
  • For others' reference, this error can also crop up when you mistakenly created a db FK using the Id field on your table instead of the foreign key field – Savage Jul 24 '18 at 13:18

1 Answers1

22

Here there are two problems, the obvious one, shown in the exception. When you define a one-to-one relationship, the FK must be also the PK. In this case the PK and FK of both entities is the Id field. The problem shown in the exception is that the FK is database generated. So, if you insert a Child1 with a related Child2, EF has no way to set the FK value of the related Child2 because it's database generated.

The second problem, that has still not arisen, is that a one-to-one relationship is only a theoric thing in a database like SQL Server. If you want to insert Child1 that depends on Child2, you need to insert first Child1, and then the related Child2. That's right, but, ooops, you also have to insert Child2 before inserting Child1, because Child1 depends also on Child2. So, having a pure one to one relationship is not possible.

To solve this problem you need to do two things:

  1. make the relationship a 1-to-(0 or 1). I.e. you must have a principal entity and a dependent entity which can or cannot exist. This will allow you to insert the principal entity, without the dependent entity, because with this configuration you can isnert the principal without the dependent.
  2. the principal PK can be left as database generated, but you have to change the PK on the dependent entity not to be db generated. So, when you insert the dependent entity the PK, which is also the FK, can be freely specified by EF.

Finally, if you think of it, a 1-to-1 relationship usually makes no sense. You can use a single table that holds all the columns in both tables, because whenver a row exists in table A, it must exists in table B and viceversa. So having a single table has the same effect.

However, if you still want to use the 1-to-1 relationship, EF allows you to model it like this:

modelBuilder.Entity<Child1>()
            .HasRequired(c1 => c1.Child2)
            .WithRequiredPrincipal(c2 => c2.Child1);

Note that, in this case, the EF abstraction takes care to allow you to have a 1-to-1 relationship, even if it cannot exists in the DB. However, it's necessary to specify this relationship using the ModelBuilder because you need to specify a principal and a dependent side. In this case the principal is Child1 and the dependent is Child2. Note that you still have to be careful with the rule for database generated values.

NOTE that this is modelled in the DB with a single FK from Child2 to Child1, and not FK from Child1 to Child2. So, in the DB is a (1)-to(0 or 1) relationship, as explained above

public class Child1
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]  // Leave as is
    public int Id { get; set; }
    ...


public class Child2
{
    //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]  // Not db-generated
    //[ForeignKey("Child1")] -- specified in the model builder
    public int Id { get; set; }
JotaBe
  • 38,030
  • 8
  • 98
  • 117
  • Thanks. The reason I use 1:1 relationship is that, we have to separate the Child1 and Child2 model due to other components need these two classes. And by the way, can you post the changed model classes to show what changes I need to adapt for above two models, since I add the model builder configuration, it still the same error. – Jerry Bian Jul 07 '14 at 09:03
  • I've updated my answer with the modified model. Rememeber that, as you're working with a 1-to-1 relationship, and `Child2` is dependent on `Child1`, its `Id` will be copied from the related `Child1` when creating it. NOTE: if you only had one table, there are other ways to solve that problem: for example you could modify the other components to require `IChild1`and `IChild2` and implement those interfaces on the only existing entity, or use mappers (AutoMapper, ValueInjecter). I don't know how your app looks like and if this would work for you. – JotaBe Jul 07 '14 at 09:18
  • It throws new exception: `{"Cannot insert explicit value for identity column in table 'Child2' when IDENTITY_INSERT is set to OFF."}` , what does it mean? The full code can be seen [here](https://gist.github.com/JerryBian/8ed6605453adaed579b2) . – Jerry Bian Jul 07 '14 at 09:34
  • Don't worry, it's under control :) That means that the `Id` field in the database is still the `identity` field for the table. Change the table on the DB so that it's no longer `identity`. (I thought you was using migrations, or synchronizing the DB with the model in some other way, so that this shouldn't happen) – JotaBe Jul 07 '14 at 09:44
  • GREAT! So the `Child2` Id is always same with `Child1` Id, right? – Jerry Bian Jul 07 '14 at 09:49
  • That's what I explained in the EF 1-to-1 relationship. EF will always do relationship fixup, so that you can take for granted that the Id will be the same. – JotaBe Jul 07 '14 at 10:50
  • I know this is an old question, but in the scenario you describe above, is there any difference to how you would create Child1 and Child2 when you're seeding data? I keep getting 0 for the Child1ID in my Child2 entity in my seeded data. – Mike Devenney Mar 22 '17 at 17:03
  • @MikeDevenney As Child2 depends on Child1, you should create Child1 first, and then create Child2 and assign it to Child1 so that EF can do relationship fixup and set the Id (FK) value on Child2. If you don't assign Child2 to Child1, the value on Child1 isn't magically set. – JotaBe Mar 22 '17 at 17:41
  • I tried that but still ran into an issue with Multiplicity. After some digging I figured out the problem. This was a one to many relationship prior to this round of changes. In setting up the entities for the 1-1, I forgot to remove the Child2Id field from the Child1 entity. In addition, the constructor for Child1 was assigning a new Child2 to a navigation property in its constructor. In the end, after removing the navigation property assignment from the constructor and removing the required Child2Id from Child1 my Seed method started working and the app behaves as expected... Long day. – Mike Devenney Mar 22 '17 at 19:43
  • (cont'd) If the explanation above causes some confusion, it may help to know that the entity on the many side of the one to many relationship is now the primary in the 1-1 due to some changes in business logic which made wrapping my head around it all the more fun. – Mike Devenney Mar 22 '17 at 19:45