2

In my code, I have 2 "tables" which have a 1 to 1 or 1 to 0 mapping. A person table and a passport table (I'm showing code that replicates my issue, so please forgive how contrived these examples are). This means a person may not have a passport, but a passport must have a person associated.

The problem I have is when I save, I get the following error message

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

I must admit, I don't actually really understand the issue here! Yes I can read the words but I don't know why it is struggling with Id column!

This is what I have

[Table("Person")]
public partial class Person
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }

    public int PassportId {get;set;}

    public virtual Passport Passport {get;set;}
}

[Table("Passport")]
public partial class Passport 
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }

    public string PassportDetail { get; set; }

    public virtual Person Person { get; set; }
}

And in my Entities (DataContext) I have

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ 
    modelBuilder.Entity<Person>()              //create for person
            .HasOptional(a => a.Passport)      //a person has optional passport
            .WithRequired(s => s.Person);      //a passport requires a person
}

Saving to the database should have been simple...

var p = new Person();
p.Passport = new Passport()
        {
              PassportDetail = "test"
        };
dataContext.Person.Add(p);
dataContext.SaveChanges();

But I am told off for the reason above.

What have I done wrong?

Posts like A dependent property in a ReferentialConstraint is mapped to a store-generated column seem to be very complex and about poorly defined tables where as I hope (famous last words) my situation is simple and I've avoided this)

The accepted answer in A dependent property in a ReferentialConstraint is mapped to a store-generated column error on 1-to-1 relationship seems shows what do!

The fix is update to

[Table("Passport")]
public partial class Passport 
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]  //changed here
    [Key]
    public int Id { get; set; }
….

However, if I do that, then when I save to my database, I'm being told off for inserting duplicates :(

MyDaftQuestions
  • 4,487
  • 17
  • 63
  • 120

2 Answers2

4

I think the quick fix you found its not the proper way to deal with EF Code first ideolegy, in order to have a one-to-one relationship you have to match each table with the other table primary key , in your case its an int Id , but for the person you want this option to be optional so you mark it as nullable and EF will generate the right scripts to allow a creation of a Person without a Passport or with and a Passport is only allowed to be created if there is an association with a person

[Table("Person")]
public partial class Person
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }

    public int? PassportId {get;set;}

    public virtual Passport Passport {get;set;}
}

[Table("Passport")]
public partial class Passport 
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }

    public string PassportDetail { get; set; }

    public int PersonId {get;set;}

    public virtual Person Person { get; set; }
}
Tiago Silva
  • 2,299
  • 14
  • 18
2

In reality one-to-one relationship is not possible in sql server. It can be one-to-zero-or-one relationship. In your case one Person will have one or zero Passport, so can simply the relationship as follows where Person in the principal entity and Passport is the dependent entity.

[Table("Person")]
public partial class Person
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public PersonName {get;set;}

    public virtual Passport Passport {get;set;}
}

[Table("Passport")]
public partial class Passport 
{
    [Key,ForeignKey("Person")]
    public int PersonId { get; set; }  //<-- here `PersonId` the is both the Primary key and foreign key

    public string PassportDetail { get; set; }

    public virtual Person Person { get; set; }
}

Note: In this set up you don't need any Fluent API configuration.

And during insert:

var person = new Person()
{
    PersonName = "Test"
}

Passport passport = new Passport()
{
      PersonId = person.Id,
      PassportDetail = "test"
};

dataContext.Persons.Add(person);
dataContext.Passports.Add(passport);
dataContext.SaveChanges();
TanvirArjel
  • 30,049
  • 14
  • 78
  • 114
  • Thank you for this... I'm panicing a little bit because I've never created a table which doesn't have an ID :( – MyDaftQuestions Feb 15 '19 at 09:01
  • Haha! Actually here `PersonId` is the `Id` and `ForeignKey` at the same time. – TanvirArjel Feb 15 '19 at 09:03
  • 2
    @AndreiDragotoniu Already I have been cleared of regarding this from the OP. He said that he will have one-to-zero or one passport. Please read the comment below the question. If One Person has more than one passport then it will be one-to-many relationship which the questioner don't want. – TanvirArjel Feb 15 '19 at 09:28
  • @AndreiDragotoniu You may have different opinion but if you want one-to-one-or-zero relationship than this is the best approach. You can check EF/EF core official documentation. – TanvirArjel Feb 15 '19 at 09:33
  • @AndreiDragotoniu here is the EF framework official documentation : https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/fluent/relationships – TanvirArjel Feb 15 '19 at 09:34
  • @AndreiDragotoniu Please read official documentation provided by EF team please! – TanvirArjel Feb 15 '19 at 09:36
  • @AndreiDragotoniu By the way. I respect your different opinion and you also have right to down vote anything if you don't like. Take care. Thank you. – TanvirArjel Feb 15 '19 at 09:39
  • 1
    Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188467/discussion-between-tanvirarjel-and-andrei-dragotoniu). – TanvirArjel Feb 15 '19 at 09:41
  • 1
    @AndreiDragotoniu I strongly disagree. What Tanvir is proposing is called [Shared Primary Key Association](https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations), is the default and preferred design for 1 to 0..1 relationships in both EF6 and EF Core. Also in EF6 it's the only fully supported one-to-one mapping - FK mapping is possible, but with limitations. Shortly, this is the best db design for such relationship. – Ivan Stoev Feb 18 '19 at 10:17
  • @MyDaftQuestions See the **Ivan Stoev** comment here. `Ivan Stoev` is one of the most expert person in `Stack Overflow` community who has very in depth knowledge in Both `EF` and `EF Core`. – TanvirArjel Feb 18 '19 at 11:09
  • @TanvirArjel: StackOverflow usually detects this sort of "serial down-voting" and then reverses it in 1-2 days, however you may want to also notify a moderator through a flag about these actions. – Hovercraft Full Of Eels Feb 18 '19 at 17:18
  • @HovercraftFullOfEels Thanks a lot! I shall wait for the reversing! If not then will flag. – TanvirArjel Feb 18 '19 at 17:19
  • @TanvirArjel: Andrei Dragotoniu shows no negative rep on his profile today, suggesting that he did not down-vote your answers. He may have a dup profile, but no way for us mortals to know. Just thought I'd let you know. – Hovercraft Full Of Eels Feb 18 '19 at 17:57
  • Down voted repo does not show on down voter profile. This is stack overflow privacy. – TanvirArjel Feb 18 '19 at 17:58
  • @HovercraftFullOfEels By the way thanks for your heartfelt concern. – TanvirArjel Feb 18 '19 at 18:02
  • His comments remain here https://chat.stackoverflow.com/rooms/188467/discussion-between-tanvirarjel-and-andrei-dragotoniu which I have screen shots of – Dave Feb 18 '19 at 20:57