Trainee4Life
Trainee4Life

Reputation: 2273

One-to-One mapping issue in EF Code First

Consider this scenario:

public class House
{
    public string Name { get; set; }
    public virtual Person Person1 { get; set; }
    public virtual Person Person2 { get; set; }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public House House { get; set; }
}

// Relationships
this.HasOptional(t => t.Person1)
    .WithRequired(t => t.House);
this.HasOptional(t => t.Person2)
    .WithRequired(t => t.House);

When I try to insert an instance of House a MetaDataException is thrown:

Schema specified is not valid. Errors: (36,6) : error 0040: Type House_Person1 is not defined in namespace Company.Models (Alias=Self).

After a little more hacking, figured that this works:

// Relationships
this.HasOptional(t => t.Person1)
    .WithRequired();
this.HasOptional(t => t.Person2)
    .WithRequired();

But then I won't have the Person.House property populated by EF. Can I emulate this behavior (shown below)?

    public virtual Person Person1
    {
        get
        {
            return _person1;
        }
        set
        {
            _person1 = value;
            _person1.Haushalt = this;
        }
    }

    public virtual Person Person2
    {
        get
        {
            return _person2;
        }
        set
        {
            _person2 = value;
            _person2.Haushalt = this;
        }
    }

Upvotes: 1

Views: 161

Answers (1)

Slauma
Slauma

Reputation: 177163

You cannot use the same navigation property (Person.House) in two different relationships (House_Person1 and House_Person2).

An additional limitation is that EF only supports shared primary key associations as true one-to-one relationships. It means that a related House and Person must have the same primary key value. But from that follows that House.Person1 and House.Person2 would need to have the same PK value as the House. In other words: Person1 and Person2 can never be different persons.

I would suggest that you map your relationship as a one-to-many relationship and ensure in your application's business logic that a House must not have more than two Persons. (Because your current mapping allows that Person1 and Person2 are optional, zero or only one person would be allowed):

public class House
{
    public string Name { get; set; }
    public virtual ICollection<Person> People { get; set; }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public House House { get; set; }
}

this.HasMany(t => t.People)
    .WithRequired(t => t.House);

Edit

If you want to keep the two Person navigation properties in House you can only map this as two one-to-many relationships (even if this relation in wrong from business viewpoint) without having a navigation property in Person (like shown here):

this.HasOptional(t => t.Person1)
    .WithMany();
this.HasOptional(t => t.Person2)
    .WithMany();

You can use the House property in Person as you did in your last code snippet, but you need to exclude it from the model:

modelBuilder.Entity<Person>()
    .Ignore(t => t.House);

Person.House isn't a navigation property anymore you can't load it (with eager or lazy loading) when you load a Person entity from the database.

Upvotes: 2

Related Questions