I've a system with several self referencing entities with one-to-many relationship (parent-child). I'd like to use the common base class for all of those entities:
public class SelfReferencing
{
public SelfReferencing Parent {get; set;}
public ICollection<SelfReferencing> Children {get; set;}
}
and inherit the particular entity from SelfReferencing. Fluent API mapping requires the reference Properties to be of the defining type, when trying to do following:
modelBuilder.Entity<ConcreteSelfReferencing>()
.HasMany(e => e.Children)
.WithOptional(e => e.Parent);
So, can you help me to find a possibility to make use of inheritance and get the entities mapped?
THX
Note: The example below is known as Table-Per-Hierarchy (TPH) - i.e. all contained in one table. Click on this link for Table-Per-Type (TPT), which has different tables for each type.
When using base types and inherited types, you have to tell EF how to determine the association for a specific inherited type.
Taking your code:
public abstract class SelfReferencing
{
public SelfReferencing Parent { get; set; }
public ICollection<SelfReferencing> Children { get; set; }
}
public class ConcreteSelfReferencing : SelfReferencing
{
}
EF now has to work out whether the sub-class is a ConcreteSelfReferencing
or any other type of sub-class. This is determined by a discriminator on the table itself, to which the column is not part of your mapping.
To take another example, similar to I've used in the past:
public abstract class Policy
{
public int Id { get; set; }
public string PolicyNumber { get; set; }
}
public class InsurancePolicy : Policy
{
}
public class CarPolicy : Policy
{
}
The table was structured like this:
| Id | PolicyNumber | Type | ..... |
1 CAR0001 C
2 ISN0001 I
To get EF to result them correctly, you would have:
public class MyContext : DbContext
{
public MyContext() : base()
{
}
public DbSet<Policy> Policies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
var policyMap = modelBuilder.Entity<Policy>();
// Set up discriminators
policyMap.Map<InsurancePolicy>(p => o.Requires("Type").HasValue("I"))
.Map<CarPolicy>(p => o.Requires("Type").HasValue("C"));
// Notice that `Type` is only used for the discriminators, not an actual
// mapped property
policyMap.HasKey(x=>x.Id);
policyMap.Property(x=>x.PolicyNumber);
}
}
And from your code, you can either do the filtering yourself, or put the filtering in the DbContext
. Here is an example from a separate class.
public class PolicyRepository
{
private MyContext context = new MyContext();
public PolicyRepository()
{
}
public IQueryable<InsurancePolicy> GetInsurancePolicies()
{
return this.context.Policies.OfType<InsurancePolicy>();
}
public IQueryable<CarPolicy> GetCarPolicies()
{
return this.context.Policies.OfType<CarPolicy>();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With