0

I'm using EF6 Code First and here's a simple model which reproduces my issue:

abstract class GeoInfo
{
    public int Id { get; set; }
    public double CoordX { get; set; }
    public double CoordY { get; set; }
}

class PersonGeoInfo : GeoInfo
{
    [Required]
    public Person Person { get; set; }
}

class CarGeoInfo : GeoInfo
{
    [Required]
    public Car Car { get; set; }
}

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual PersonGeoInfo PersonGeoInfo { get; set; }
}

class Car
{
    public int Id { get; set; }
    public string Number { get; set; }
    public virtual CarGeoInfo CarGeoInfo { get; set; }
}

And a context:

class MyContext : DbContext
{
    public DbSet<GeoInfo> GeoInfos { get; set; }
    public DbSet<PersonGeoInfo> PersonGeoInfos { get; set; }
    public DbSet<CarGeoInfo> CarGeoInfos { get; set; }
    public DbSet<Person> Persons { get; set; }
    public DbSet<Car> Cars { get; set; }
}

Entity Framework generates this database:

db schema

Look at GeoInfoes foreign keys constraints. They both are in one column and using this database is imposible. But EF didn't warned me, it just throws database exception: The INSERT statement conflicted with the FOREIGN KEY...

I've tried to use TPT strategy - the same problem, but mixing is between association and inheritance keys.

I've tried to explicitly define foreign keys in model - didn't help. Nothing stops EF from generating a FK constraint in the same PK column.

I can't create a base class for a Car and a Person because in real app they are already participate in another hierarchies.

Am I using Entity Framework wrong or it really can't map one-to-one relationship along with inheritance to a database?

astef
  • 8,575
  • 4
  • 56
  • 95
  • What do you need the `CarGeoInfo` and `PersonGeoInfo` for? The `PK, FK` is because of one-to-one relationship. If you want to have separate keys, remove the `virtual` properties from your `models`, this will generate `FK` columns in the `GeoInfo` table. – Lukas G Jan 26 '15 at 15:24
  • @Ghukas No, it will not – astef Jan 26 '15 at 15:35
  • Why a TPT strategy does not adapt to your escenario? – ocuenca Jan 26 '15 at 15:43
  • Yes, it will, I've used the classes from your example without the `virtual` properties. EF has generated the Foreign Keys for both `Cars` and `People` entities. I'm using EF 6.1.2 – Lukas G Jan 26 '15 at 15:44
  • @Ghukas It's strange, I've just tried it too. Can you provide full example code as an answer? – astef Jan 26 '15 at 15:46
  • @octavioccl If TPT, the FK to the base class table mixing with the association FK in the same PK column. To enable TPT change my example by providing `TableAttribute` to a `PersonGeoInfo` and `CarGeoInfo` – astef Jan 26 '15 at 15:49
  • @Ghukas Ah, I see what you mean. You want to delete to whole navigation property. I thought you want to remove `virtual` keyword. Yes, of course, no property - no problem =) – astef Jan 26 '15 at 15:51
  • @Ghukas I need to be able to retrieve coordinates by person's or car's id, and to retrieve person or a car knowing the `GeoInfo.Id` – astef Jan 26 '15 at 15:55
  • OK, what's the reason you do not want to use the Foreign Keys? Having the `ID` of the `GeoInfo`, you are always able to find the appropriate `Cars` and `Persons` by the generated Foreign Keys http://i.imgur.com/TVTYaq1.png The thing is now you have to use the relations as one-to-many, however there always will be one entity related. – Lukas G Jan 26 '15 at 16:04
  • @Ghukas Yeah, I've just understood that I don't need that nav props. Db will keep one-to-many relation, but EF can ensure it will be one-to-one using [Required] attribute. Thank you very much – astef Jan 26 '15 at 16:22

1 Answers1

1

I think you can resolve your problem with this model:

public class GeoInfo
{
    public int Id { get; set; }
    public double CoordX { get; set; }
    public double CoordY { get; set; }

}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("PersonGeoInfo")]
    public int? PersonGeoInfoId { get; set; }

    public virtual GeoInfo PersonGeoInfo { get; set; }
}

public class Car
{
    public int Id { get; set; }
    public string Number { get; set; }

    [ForeignKey("CarGeoInfo")]
    public int? CarGeoInfoId { get; set; }

    public virtual GeoInfo CarGeoInfo { get; set; }
}

This way, a Person and a Car are associated with a GeoInfo, and and when you want to find a person by their coordinates, you could do this:

 int geoInfoId = 3;
 var person=_db.Persons.FirstOrDefault(p=>p.PersonGeoInfoId==geoInfoId);

But as you can see, with this model you are going to have one-to many relationships between Person and GeoInfo and Car and GeoInfo. I think this model could be more real because, eg, two people could have the same coordinates.

ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • Well, in my case it's imposible to have two people at one `GeoInfo`, but in general, idea is right - we can go to a one-to-many relationships from one-to-zero-or-one – astef Jan 27 '15 at 07:06