6

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

toppless
  • 411
  • 1
  • 6
  • 16
  • Show your `ConcreteSelfReferencing` – haim770 Jun 02 '14 at 15:32
  • @haim770 public class ConcreteSelfReferencing : SelfReferencing {} – toppless Jun 02 '14 at 15:34
  • 1
    You need to map using the base class: `modelBuilder.Entity()`. – haim770 Jun 02 '14 at 15:38
  • @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 Jun 03 '14 at 14:00
  • Have you looked in StackOverflow for other questions. There are loads: http://stackoverflow.com/questions/9955491/self-referencing-parent-child-relationship-in-entity-framework – Dominic Zukiewicz Jun 03 '14 at 15:52
  • @DominicZukiewicz the only question on SO going in the same direction i found is http://stackoverflow.com/questions/22983722/notsupportedexception-the-type-a-cannot-be-mapped-as-definede-table-per-concre but the provided solution is not the solution in my case. Actually started a thread on Codeplex EF Forum and shared my updated thought on the case there https://entityframework.codeplex.com/discussions/547418 – toppless Jun 04 '14 at 08:13

1 Answers1

4

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>();
   }
}
Dominic Zukiewicz
  • 8,258
  • 8
  • 43
  • 61
  • Thank you for such detailed answer. But this doesn't seem to match in my case, cause, as mentioned before, I need a table per concrete entity and cannot hold different entity types in the same table. – toppless Jun 04 '14 at 11:55
  • @toppless: See revised answer at the top, linking to some walkthroughs of the TPT type, which sounds like what you are after. – Dominic Zukiewicz Jun 04 '14 at 13:23
  • unfortunately the provided link doesn't provide the solution for my problem. – toppless Jun 11 '14 at 11:55
  • Can you update your question with what you would want the final solution to does.? For example, the queries you would do on it, any thoughts on how it 'should' appear to work? – Dominic Zukiewicz Jun 11 '14 at 17:29