Missing Id on insert of one to one-or-zero with En

2019-08-23 04:20发布

问题:

Using EF6 against SQL server, when saving a record with a one to zero-or-one relationship, how do the mappings need to be configured in order for the id of the "One"' to be set in the "zero-or-one"

A simple case for this would be:

public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    public int CarId { get; set; }
    public string Make { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }
}

With mapping configurations

public class PersonMap : EntityTypeConfiguration<Person>
{
    public PersonMap()
    {
        HasKey(x => x.PersonId);
        HasOptional(x => x.Car)
            .WithRequired(x => x.Person);
    }
}

public class CarMap : EntityTypeConfiguration<Car>
{
    public CarMap()
    {
        HasKey(x => x.CarId);
        Property(x => x.Make).IsRequired();
        HasRequired(x => x.Person)
            .WithOptional(x => x.Car);
    }
}

Running a very simple test against any variations of the mappings above always results in the Car entity having a PersonId of null

[TestFixture]
public class PersonMappingTest
{
    [Test]
    public void ItWorks()
    {
        var person = new Person {Name = "Test person"};
        var car = new Car {Person = person, Make = "Ford"};
        person.Car = car;
        car.Person = person;

        using (var cx = new CompWalkContext())
        {
            cx.Persons.Add(person);
            cx.SaveChanges();
        }

    }
}

-

CarId   Make    PersonId
-----   ----    --------
1       Ford    NULL
2       Ford    NULL

Is this possible, or does the Person entity need to be saved prior to adding the Car?

回答1:

You don't want the double-mapping, and your keys are a bit gunked up. A car needs a person, so the PersonID would not be nullable, where-as a person may, or may not have a car. if you want the PersonId on Car, it's a 1-0..1 between person and car. From here you set up the HasRequired on the Car WithOptional but you need to tell EF about the key to use. In the DB, PersonID on the Car table needs to be not-nullable.

public class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    public int CarId { get; set; }
    public string Make { get; set; }
    public virtual Person Person { get; set; }
}
public class PersonMap : EntityTypeConfiguration<Person>
{
    public PersonMap()
    {
        HasKey(x => x.PersonId);
    }
}

public class CarMap : EntityTypeConfiguration<Car>
{
    public CarMap()
    {
        HasKey(x => x.CarId);
        Property(x => x.Make).IsRequired();
        HasRequired(x => x.Person)
            .WithOptional(x => x.Car)
            .Map(x => x.MapKey("PersonId")); // Tell EF to use the PersonId on the Car to resolve the Person reference.
    }
}

Then when you go to create a person and car...

var person = new Person { /* set properties */ };
dbContext.Persons.Add(person);
// Or retrieve your Person from the DbSet..

var car = new Car
{
   /* set properties... */
   Person = person // associate the person to the car.
};
dbContext.Cars.Add(car);
dbContext.SaveChanges();

Now if you retrieve a car, you can access their .Person. If you retrieve a person that has a car, you can access their .Car.