6
votes

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

1
Show your ConcreteSelfReferencinghaim770
@haim770 public class ConcreteSelfReferencing : SelfReferencing {}toppless
You need to map using the base class: modelBuilder.Entity<SelfReferencing>().haim770
@haim770 Could you provide a complete mapping solution, because actually i'm getting this error: System.NotSupportedException: The type 'ConcreteSelfReferencing' cannot be mapped as defined because it maps inherited properties from types that use entity splitting or another form of inheritance. Either choose a different inheritance mapping strategy so as to not map inherited properties, or change all types in the hierarchy to map inherited properties and to not use splitting. I'm trying to map using Table per Concrete Type strategy.toppless
Have you looked in StackOverflow for other questions. There are loads: stackoverflow.com/questions/9955491/…Dominic Zukiewicz

1 Answers

4
votes

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>();
   }
}