代码优先: 独立协会与外国密钥协会?

每当我开始做一个新项目,设计我的 POCOs 时,我都会和自己进行一场心理上的辩论。我看到过很多教程/代码示例似乎更喜欢 外国钥匙协会外国钥匙协会:

外键关联

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

独立协会相反:

独立协会

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

我过去曾使用过 NHibernate,并使用过独立的关联,它们不仅感觉更像面向对象,而且(使用延迟加载)具有访问整个 Customer 对象的优势,而不仅仅是它的 ID。例如,这允许我检索 Order 实例,然后执行 Order.Customer.FirstName,而不必显式地执行连接,这非常方便。

总结一下,我的问题是:

  1. 有没有什么重大的缺点 使用独立协会? 还有..。
  2. 如果没有的话 会是使用外键关联的原因吗?
34464 次浏览

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.

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.

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.

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.