Justin Grant
Justin Grant

Reputation: 46723

Code-First Entity Framework polymorphic relationships if one derived type contains another

Using EF6 Code-First in a party-planning app, I'm trying to model a Party entity with an Attendees property (e.g. ICollection<???> Attendees) where attendees can either be a Person or a Family and where a Family contains a collection of Person instances.

I've read through the helpful series of Code-First modeling articles here: http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-1-introduction-and-basic-concepts. If attendees were Dog or Cat or some other mutually-exclusive, unrelated types then the advice in those articles is clear.

But what's the right way to model a collection of entities that may be related to each other like Person and Family are?

EDIT: The main problem I'm trying to solve here is how to model the ICollection<???> Attendees property of the Party entity. Specifically, the articles linked above give advice (quoted below) about choosing a modeling strategy for inheritance. In my use-case, one derived type (Family) contains instances from another derived type (Person). Does this suggest that I should use one of the strategies over the other? Or is this fact not very relevant to choice of modeling strategy?

There are three different approaches to representing an inheritance hierarchy:

  • Table per Hierarchy (TPH): Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.
  • Table per Type (TPT): Represent "is a" (inheritance) relationships as "has a" (foreign key) relationships.
  • Table per Concrete class (TPC): Discard polymorphism and inheritance relationships completely from the SQL schema.

<snip>

Before we get into this discussion, I want to emphasize that there is no one single "best strategy fits all scenarios" exists. As you saw, each of the approaches have their own advantages and drawbacks. Here are some rules of thumb to identify the best strategy in a particular scenario:

  • If you don’t require polymorphic associations or queries, lean toward TPC—in other words, if you never or rarely query for BillingDetails and you have no class that has an association to BillingDetail base class. I recommend TPC (only) for the top level of your class hierarchy, where polymorphism isn’t usually required, and when modification of the base class in the future is unlikely.

  • If you do require polymorphic associations or queries, and subclasses declare relatively few properties (particularly if the main difference between subclasses is in their behavior), lean toward TPH. Your goal is to minimize the number of nullable columns and to convince yourself (and your DBA) that a denormalized schema won’t create problems in the long run.

  • If you do require polymorphic associations or queries, and subclasses declare many properties (subclasses differ mainly by the data they hold), lean toward TPT. Or, depending on the width and depth of your inheritance hierarchy and the possible cost of joins versus unions, use TPC.

By default, choose TPH only for simple problems. For more complex cases (or when you’re overruled by a data modeler insisting on the importance of nullability constraints and normalization), you should consider the TPT strategy. But at that point, ask yourself whether it may not be better to remodel inheritance as delegation in the object model (delegation is a way of making composition as powerful for reuse as inheritance). Complex inheritance is often best avoided for all sorts of reasons unrelated to persistence or ORM. EF acts as a buffer between the domain and relational models, but that doesn’t mean you can ignore persistence concerns when designing your classes.

Upvotes: 0

Views: 545

Answers (1)

Uthman Rahimi
Uthman Rahimi

Reputation: 708

your model look like this :

 public class Family
{
    public int Id { get; set; }
    public string Name { get; set; }
public ICollection<Person> People { get; set; }

}
public class Person 
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Family Family { get; set; }
}

and add some config like this:

   HasRequired(row => row.Family).WithMany().HasForeignKey(row => row.FamilyId).WillCascadeOnDelete(false);

Upvotes: 0

Related Questions