Reputation: 25
Please take a look at following code:
public class SomeEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class SomeEntityA : SomeEntity
{
public int Number { get; set; }
}
public class SomeEntityB : SomeEntity
{
public string Text { get; set; }
}
public class User
{
public int Id { get; set; }
public int Username { get; set; }
public virtual ICollection<SomeEntityA> SomeEntitiesA { get; set; }
public virtual ICollection<SomeEntityB> SomeEntitiesB { get; set; }
}
My question is - is there a way to set the FluentApi to make relationships shown above work properly? Currently when new SomeEntityA object is being added to User, EF creates a new record in SomeEntities table with properly set User_Id FK, however in SomeEntitesA which is an inherited table, there's also a FK property User_Id - set as null and when I try to get SomeEntitesA collection from User object - it's empty. I do realize why that happens, but I'm not sure whether there's a way to fix this? The only solution that comes to my mind at this moment is to replace following code:
public virtual ICollection<SomeEntityA> SomeEntitiesA { get; set; }
public virtual ICollection<SomeEntityB> SomeEntitiesB { get; set; }
with:
public virtual ICollection<SomeEntity> SomeEntitiesA { get; set; }
public virtual ICollection<SomeEntity> SomeEntitiesB { get; set; }
and configure FluentApi.
Any thoughts would be highly appreciated.
Upvotes: 1
Views: 213
Reputation: 10221
Have a look at this question/answer, the classes have exactly the same structure as yours and there is a detailed explanation why things are not working as expected:
Is inheritance of navigation properties supported?
As Slauma pointed out, there is a rather simple solution to get around this by doing the following (copied from the linked answer and fit to your example):
public class User
{
public int Id { get; set; }
public int Username { get; set; }
// this is necessary to have access to the related SomeEntityAs/SomeEntityBs
// also it cant be private otherwise EF will not overload it properly
public virtual ICollection<SomeEntity> SomeEntities { get; set; }
public IEnumerable<SomeEntityA> SomeEntitiesA { get { return this.SomeEntities.OfType<SomeEntityA>(); } }
public IEnumerable<SomeEntityB> SomeEntitiesB { get { return this.SomeEntities.OfType<SomeEntityA>(); } }
}
Upvotes: 1
Reputation: 177133
Your proposed solution also won't work because you cannot relate two navigation properties User.SomeEntitiesA
and User.SomeEntitiesB
to the same endpoint SomeEntity.User
. You would actually need two users:
public class SomeEntity
{
public int Id { get; set; }
public int UserAId { get; set; }
[InverseProperty("SomeEntitiesA")]
public virtual User UserA { get; set; }
public int UserBId { get; set; }
[InverseProperty("SomeEntitiesB")]
public virtual User UserB { get; set; }
}
But then you could also keep the collection types as they are and move the users to the derived entities:
public class SomeEntity
{
public int Id { get; set; }
}
public class SomeEntityA : SomeEntity
{
public int Number { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class SomeEntityB : SomeEntity
{
public string Text { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
public class User
{
public int Id { get; set; }
public int Username { get; set; }
public virtual ICollection<SomeEntityA> SomeEntitiesA { get; set; }
public virtual ICollection<SomeEntityB> SomeEntitiesB { get; set; }
}
You don't need to specify the [InverseProperty]
in this case because conventions will detect now the correct pairs of navigation properties.
Upvotes: 1