Код по-перше: Незалежні асоціації проти закордонних ключових асоціацій?


102

У мене є душевна дискусія із самим собою щоразу, коли я починаю працювати над новим проектом і розробляю свої 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, але і (з ледачим завантаженням) мають перевагу в наданні мені доступу до всього об'єкта Клієнта, а не лише до його ідентифікатора. Це дозволяє мені, наприклад, отримати екземпляр Order, а потім зробити Order.Customer.FirstNameбез явного виконання з'єднання, що надзвичайно зручно.

Отже, підводячи підсумки, мої запитання:

  1. Чи є суттєві недоліки у використанні незалежних асоціацій? і ...
  2. Якщо таких немає, то що було б причиною взагалі використання іноземних ключових асоціацій?

Відповіді:


106

Якщо ви хочете повністю скористатися ORM, ви обов'язково скористаєтесь посиланням Entity:

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

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

Entity Framework v4 пропонує новий тип асоціацій під назвою Асоціація закордонних ключів. Найбільш очевидною відмінністю між незалежною та зовнішньою ключовою асоціацією є клас замовлення:

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 повинно бути унікальним. Тому при використанні об'єднання іноземних ключів ви також повинні призначити тимчасові унікальні ідентифікатори новоствореним особам, які використовуються у відносинах.
  • Ця асоціація не відображається, але натомість визначає референтні обмеження.

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

Редагувати:

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


Дякуємо за Вашу проникливу відповідь, а також за те, що вони зрозуміли правильну термінологію, яка допомогла мені знайти багато ресурсів на цю тему та плюси обох методів.
Даніель Люцці

1
Я щойно натрапив на вашу статтю, Ладиславе. Дуже цікаве читання та чудовий ресурс для подальшого розуміння різниці між цими двома підходами. Ура.
Даніель Люцці

1
@GaussZ: Наскільки я знаю, не було жодних змін у способі поводження з асоціаціями після EF4 (де були запроваджені асоціації FK).
Ladislav Mrnka

1
Цей та інші відповіді, схоже, не торкаються результатів роботи. Однак, згідно з розділом 2.2 Фактори, які впливають на продуктивність покоління View View із статті MSDN, використання Незалежної асоціації, схоже, збільшує витрати на генерування перегляду над закордонними ключовими асоціаціями.
Веверке

1
@LadislavMrnka: чи можете ви двічі перевірити посилання на текст, який ви згадали вище, працює? Я не можу отримати доступ до нього.
Veverke

34

Використовуйте обидва. І зробіть посилання вашої особи віртуальними, щоб забезпечити ледачий завантаження. Подобається це:

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

Це заощаджує на непотрібних пошукових запитах БД, дозволяє ледаче завантажувати і дозволяє легко бачити / встановлювати ідентифікатор, якщо ви знаєте, яким ви хочете це бути. Зауважте, що наявність обох жодним чином не змінює структуру вашої таблиці.


5
Домовились. Це я і закінчив, як запропонував Ладіслав. Це дійсно дає найкраще з обох світів; весь об'єкт, коли вам потрібні всі його властивості, і його ідентифікатор, коли вам потрібен лише ПК та не піклується про інше.
Даніель Люцці

9

Незалежна асоціація не справляється з тим, 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);

Ми очікуємо поведінку, існуючий клієнт буде пов'язаний з новим замовленням.


2
Це гарна знахідка. Однак обхід (і я вважаю правильним способом) прив’язати клієнта до замовлення - це завантаження його з контексту db, наприклад, такий: var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; Або ви можете використовувати метод Select для завантаження клієнта з контексту db. Це працює з незалежною асоціацією.
tala9999

4

Я віддаю перевагу об’єктному підходу, щоб уникнути зайвих пошуків. Об'єкти властивості можна так само легко заповнити, коли ви викликаєте заводський метод для створення цілого об'єкта (використовуючи простий код зворотного виклику для вкладених об'єктів). Я не бачу жодних недоліків, окрім використання пам’яті (але ви б кешували об’єкти правильно?). Отже, все, що ви робите, - це замінити стек на купу і зробити приріст від продуктивності, не виконуючи пошуку. Сподіваюся, це має сенс.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.