Кодекс первый: независимые ассоциации против ассоциаций иностранных ключей?

Я мысленно спорю с собой каждый раз, когда начинаю работать над новым проектом и разрабатываю свои POCO. Я видел много обучающих программ / примеров кода, которые, кажется, одобряют ассоциации внешнего ключа:

Ассоциация внешнего ключа

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 и использовал независимые ассоциации, которые не только чувствуют себя в большей степени OO, но и (с ленивой загрузкой) имеют преимущество, предоставляя мне доступ ко всему объекту Customer, а не только к его ID. Это позволяет мне, например, получить экземпляр Order, а затем сделать Order.Customer.FirstName без необходимости явно объединять, что крайне удобно.

Итак, подведем итог, мои вопросы:

  1. Есть ли существенные недостатки в использовании независимых ассоциаций? а также...
  2. Если их нет, что вообще может быть причиной использования ассоциаций внешних ключей?

4 ответа

Решение

Если вы хотите в полной мере использовать ORM, вы обязательно воспользуетесь ссылкой на сущность:

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

Когда вы генерируете модель сущности из базы данных с FK, она всегда будет генерировать ссылки на сущности. Если вы не хотите их использовать, вы должны вручную изменить файл EDMX и добавить свойства, представляющие FK. По крайней мере, так было в Entity Framework v1, где допускались только независимые ассоциации.

Entity Framework v4 предлагает новый тип ассоциации, называемый Связью с внешним ключом. Наиболее очевидная разница между независимым и внешним ключом ассоциации в классе Order:

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

Как видите, у вас есть и свойство FK, и ссылка на сущность. Есть больше различий между двумя типами ассоциаций:

Независимая ассоциация

  • Он представлен как отдельный объект в ObjectStateManager, Имеет свой EntityState!
  • При создании ассоциации вам всегда нужны права с обоих концов ассоциации
  • Эта связь отображается так же, как и сущность.

Ассоциация внешнего ключа

  • Он не представлен как отдельный объект в ObjectStateManager, В связи с этим вы должны соблюдать некоторые особые правила.
  • При создании ассоциации вам не нужны оба конца ассоциации. Достаточно иметь дочернюю сущность и PK родительской сущности, но значение PK должно быть уникальным. Поэтому при использовании связи с внешними ключами вы также должны назначить временные уникальные идентификаторы вновь создаваемым объектам, используемым в отношениях.
  • Эта связь не отображается, но вместо этого она определяет ссылочные ограничения.

Если вы хотите использовать связь с внешним ключом, установите флажок Включить столбцы внешнего ключа в модель в Мастере модели данных сущностей.

Редактировать:

Я обнаружил, что разница между этими двумя типами ассоциаций не очень хорошо известна, поэтому я написал небольшую статью, в которой об этом было сказано больше подробностей и моего собственного мнения по этому поводу.

Используйте оба. И сделайте ваши ссылки на сущности виртуальными, чтобы обеспечить ленивую загрузку. Как это:

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

Это экономит на ненужных поисках БД, позволяет выполнять отложенную загрузку и позволяет легко увидеть / установить идентификатор, если вы знаете, каким он должен быть. Обратите внимание, что наличие обоих никак не меняет структуру вашей таблицы.

Независимая ассоциация плохо работает с AddOrUpdate что обычно используется в Seed метод. Когда ссылка является существующим элементом, она будет вставлена ​​повторно.

// 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);

В результате существующий клиент будет повторно вставлен, а новый (повторно вставленный) клиент будет связан с новым заказом.


Если только мы не используем ассоциацию с внешним ключом и не назначаем идентификатор.

 // 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);

У нас ожидаемое поведение, существующий клиент будет связан с новым заказом.

Я предпочитаю объектный подход, чтобы избежать ненужных поисков. Объекты свойств могут быть так же легко заполнены, когда вы вызываете ваш фабричный метод для построения всей сущности (используя простой код обратного вызова для вложенных сущностей). Я не вижу никаких недостатков, кроме использования памяти (но вы бы кэшировали свои объекты правильно?). Таким образом, все, что вы делаете, это подставляете стек для кучи и получаете выигрыш в производительности, если не выполнять поиск. Я надеюсь это имеет смысл.

Другие вопросы по тегам