Reputation: 181
I'm trying to create a navigation property in EF Core that would setup its reference conditionally based off the values of two properties. I'm not sure if this is even possible.
Let me show you an example: let's say I have a hierarchical structure of entities, such as Country
, State
, County
, and City
. I also have an entity called Law
, which could be "owned" by any of the hierarchical entities.
So, I create this enum:
public enum OwnerType
{
Country,
State,
County,
City
}
...and the Law
class:
public class Law
{
public int Id { get; set; }
public OwnerType OwnerType { get; set; }
public int OwnerId { get; set; }
}
Now I want to setup the Law
class to have a navigation property that would link OwnerId
to the Primary Key of the corresponding entity based off of the OwnerType
value.
I considered adding this to the Law
class:
public virtual object Owner { get; set; }
Or to create an IOwner
interface that each of the hierarchical entities would implement and then I'd add this instead:
public virtual IOwner Owner { get; set; }
But then I have no idea how to setup the EntityTypeConfiguration
with the EntityTypeBuilder
. This obviously won't work:
builder.HasOne(x => x.Owner).WithMany(x => x.Laws).HasForeignKey(x => x.OwnerId);
I really just have no idea how to accomplish what I'm trying to do here. Any ideas?
Upvotes: 3
Views: 1262
Reputation: 879
As I can see you have 4 different relations and you want to handle them with one foreign key what's a bad idea in concept. If you have 4 relations - you need to have 4 FKs.
In pure OOP you could use and IOwner
interface but Entity Framework requires explicit information to map your relations respectively and I believe that's the best way. Just add 4 different nullable FKs and validate Law
state with OwnerType
value.
public class Law {
public int Id { get; set; }
public OwnerType OwnerType { get; set; }
[ForeignKey(nameof(Country)]
public int? CountryId { get; set; }
public Country Country { get; set; }
[ForeignKey(nameof(State)]
public int? StateId { get; set; }
public State State { get; set; }
[ForeignKey(nameof(County)]
public int? CountyId { get; set; }
public County County { get; set; }
[ForeignKey(nameof(City)]
public int? CityId { get; set; }
public City City { get; set; }
private void Validate() {
switch (OwnerType)
{
case OwnerType.Coutnry:
if(CountryId == null)
throw new LawValidationException("Country is requried");
break;
case OwnerType.State:
if(StateId == null)
throw new LawValidationException("State is requried");
break;
case OwnerType.County:
if(CountyId == null)
throw new LawValidationException("County is requried");
break;
case OwnerType.City:
if(CityId == null)
throw new LawValidationException("City is requried");
break;
default:
throw new LawValidationException("Invalid law owner type");
}
}
}
This approach solves your problem, perfectly fits to Entity Framework abilities and can be easily integrated into external logic including unit tests.
Upvotes: 1