Code First: Independent associations vs. Foreign key associations?
Asked Answered
S

4

107

I have a mental debate with myself every time I start working on a new project and I am designing my POCOs. I have seen many tutorials/code samples that seem to favor foreign key associations:

Foreign key association

public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; } // <-- Customer ID
    ...
}

As opposed to independent associations:

Independent association

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

I have worked with NHibernate in the past, and used independent associations, which not only feel more OO, but also (with lazy loading) have the advantage of giving me access to the whole Customer object, instead of just its ID. This allows me to, for example, retrieve an Order instance and then do Order.Customer.FirstName without having to do a join explicitly, which is extremely convenient.

So to recap, my questions are:

  1. Are there any significant disadvantages in using independent associations? and...
  2. If there aren't any, what would be the reason of using foreign key associations at all?
Scuta answered 12/3, 2011 at 10:35 Comment(0)
J
109

If you want to take full advantage of ORM you will definitely use Entity reference:

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Once you generate an entity model from a database with FKs it will always generate entity references. If you don't want to use them you must manually modify the EDMX file and add properties representing FKs. At least this was the case in Entity Framework v1 where only Independent associations were allowed.

Entity framework v4 offers a new type of association called Foreign key association. The most obvious difference between the independent and the foreign key association is in Order class:

public class Order
{
    public int ID { get; set; }
    public int CustomerId { get; set; }  // <-- Customer ID
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

As you can see you have both FK property and entity reference. There are more differences between two types of associations:

Independent association

  • It is represented as separate object in ObjectStateManager. It has its own EntityState!
  • When building association you always need entitites from both ends of association
  • This association is mapped in the same way as entity.

Foreign key association

  • It is not represented as separate object in ObjectStateManager. Due to that you must follow some special rules.
  • When building association you don't need both ends of association. It is enough to have child entity and PK of parent entity but PK value must be unique. So when using foreign keys association you must also assign temporary unique IDs to newly generated entities used in relations.
  • This association is not mapped but instead it defines referential constraints.

If you want to use foreign key association you must tick Include foreign key columns in the model in Entity Data Model Wizard.

Edit:

I found that the difference between these two types of associations is not very well known so I wrote a short article covering this with more details and my own opinion about this.

Joshi answered 12/3, 2011 at 11:43 Comment(7)
Thanks for your very insightful answer, and also for the heads up on the correct terminology, which helped me find plenty of resources on the subject and the pro/cons of both techniques.Scuta
I just came across your article, Ladislav. Very interesting read, and great resource to further understand the difference between these two approaches. Cheers.Scuta
Many thanks for all your awesome posts on EF. Keep up the good work! Is there any news on the topic of independent associations in the new EF versions?Egocentrism
@GaussZ: As I know there wasn't any change in the way how are associations handled since EF4 (where FK associations were introduced).Joshi
Thanks a lot for this precious information. I was struggling to find any references because I was not finding the correct terminology. Now I can find many other resources.Viscera
This and the other answers do not seem to touch performance concern. However, according to section 2.2 Factors that affect View Generation performance from an MSDN article, using Independent Association seems to increase the cost of View Generation over Foreign Key associations.Eccentricity
@LadislavMrnka: can you double check the link to the artice you mention above is working ? I cannot access it.Eccentricity
S
35

Use both. And make your entity references virtual to allow for lazy loading. Like this:

public class Order
{
  public int ID { get; set; }
  public int CustomerID { get; set; }
  public virtual Customer Customer { get; set; } // <-- Customer object
  ...
}

This saves on unnecessary DB lookups, allows lazy loading, and allows you to easily see/set the ID if you know what you want it to be. Note that having both does not change your table structure in any way.

Selfsatisfied answered 13/5, 2011 at 15:59 Comment(2)
Agreed. This is what I ended up doing, as Ladislav suggested. It really gives you the best of both worlds; the whole object when you need all its properties, and its ID when you only need the PK and don't care about the rest.Scuta
From the documentation 2021: Because lazy loading makes it extremely easy to inadvertently trigger the N+1 problem, it is recommended to avoid it. Eager or explicit loading make it very clear in the source code when a database roundtrip occurs. https://learn.microsoft.com/en-us/ef/core/performance/efficient-querying#beware-of-lazy-loadingJuror
C
11

Independent association doesn't work well with AddOrUpdate that is usually used in Seed method. When the reference is an existing item, it will be re-inserted.

// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);

The result is existing customer will be re-inserted and new (re-inserted) customer will be associated with new order.


Unless we use the foreign key association and assign the id.

 // Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);

We have the expected behavior, existing customer will be associated with new order.

Centrifugal answered 18/8, 2014 at 6:19 Comment(1)
This is a good finding. However, the work-around (and I think the right way) to attach a customer to order is loading it from db context like this: var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; Or you can use Select method to load the customer from db context. This works with independent association.Vernita
G
4

I favour the object approach to avoid unnecessary lookups. The property objects can be just as easily populated when you call your factory method to build the whole entity (using simple callback code for nested entities). There are no disadvantages that I can see except for memory usage (but you would cache your objects right?). So, all you are doing is substituting the stack for the heap and making a performance gain from not performing lookups. I hope this makes sense.

Gond answered 12/3, 2011 at 10:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.