Entity Framework вопрос многие ко многим - PullRequest
8 голосов
/ 02 июля 2010

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

Каждая комбинация компании / продукта имеет уникальный SKU.Например, у виджетов Acme есть SKU 123, а у виджетов Omega - SKU 456. Я добавил SKU в качестве поля в промежуточную таблицу Company_Product.

EF сгенерировал модель с отношением 1: * между таблицами company и Company_Productи отношения 1: * между таблицами product и Company_Product.Я действительно хочу отношения : между компанией и продуктом.Но, самое главное, нет способа получить доступ к SKU напрямую из модели.

Нужно ли поместить SKU в свою собственную таблицу и написать объединение, или есть лучший способ?

Ответы [ 2 ]

33 голосов
/ 02 июля 2010

Я только что проверил это в новом проекте VS2010 (EFv4), и вот что я нашел:

Когда ваша ассоциативная таблица посередине (Company_Product) имеет ТОЛЬКО 2 внешних ключа к другим таблицам (CompanyID и ProductID), то добавление всех 3 таблиц в конструктор приводит к моделированию отношения многие ко многим. Он даже не генерирует класс для таблицы Company_Product. У каждой компании есть коллекция продуктов, а у каждого продукта - коллекция компаний.

Однако, если ваша ассоциативная таблица (Company_Product) имеет другие поля (например, SKU, собственный первичный ключ или другие описательные поля, такие как даты, описания и т. Д.), Тогда разработчик моделей EF создаст отдельный класс и сделает что ты уже видел.

Наличие класса посередине с отношениями 1: * между Компанией и Продуктом - неплохая вещь, и вы все равно можете получить нужные данные с помощью простых запросов.

// Get all products for Company with ID = 1
var q =
    from compProd in context.Company_Product
    where compProd.CompanyID == 1
    select compProd.Product;

Правда, не так просто перемещаться по отношениям модели, когда, например, у вас уже загружены объекты сущностей, но для этого и нужен слой данных. Инкапсулируйте запросы, которые получают данные, которые вы хотите. Если вы действительно хотите избавиться от этого среднего класса Company_Product и иметь непосредственное представление «многие ко многим» в модели класса, то вам придется свернуть таблицу Company_Product, содержащую только 2 внешних ключа, и избавиться от нее. артикула. ​​

На самом деле, я не должен говорить, что вы ДОЛЖНЫ это делать ... возможно, вы сможете внести некоторые изменения в конструкторе и настроить его так или иначе. Я попробую и доложу.

UPDATE

Сохраняя SKU в таблице Company_Product (т.е. моя модель EF имела 3 класса, а не 2; она создала класс Company_Payload с 1: * для двух других таблиц), я попытался добавить ассоциацию непосредственно между Company и Товар. Шаги, которые я следовал, были:

  • Щелкните правой кнопкой мыши на классе Компании в конструкторе
  • Добавить> Ассоциация
  • Установите "Конец" слева, чтобы быть Company (это должно быть уже)
  • Установите "Конец" справа на Product
  • Изменить обе кратности на "* (Многие)"
  • Свойства навигации должны называться «Продукты» и «Компании»
  • Хит ОК.
  • Щелкните правой кнопкой мыши на ассоциации в модели> щелкните «Table Mapping»
  • В разделе «Добавить таблицу или представление» выберите «Company_Product»
  • Карта компании -> ID (слева) и CompanyID (справа)
  • Карта продукта -> ID (слева) и ProductID (справа)

Но это не работает. Это дает эту ошибку: Ошибка 3025: проблема в сопоставлении фрагментов, начинающихся со строки 175: необходимо указать сопоставление для всех ключевых свойств (Company_Product.SKU) таблицы Company_Product.

Так что это конкретное сопоставление недопустимо, поскольку оно использует Company_Product в качестве таблицы, но не сопоставляет поле SKU с чем-либо.

Кроме того, в то время как я исследовал это, я натолкнулся на этот лакомый кусочек «Лучшей практики» из книги Entip Framework 4.0 Recipies (обратите внимание, что для таблицы ассоциации с дополнительными полями, помимо 2 FK, они ссылаются на дополнительные поля как «полезная нагрузка». В вашем случае, SKU - полезная нагрузка в Company_Product).

Лучшая практика

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

Итак, вот лучшая практика.Если у вас есть отношение «многие-ко-многим» без полезной нагрузки, и вы думаете, что есть вероятность, что со временем оно может измениться, чтобы включить полезную нагрузку, начните с дополнительного столбца идентификаторов в таблице ссылок.Когда вы импортируете таблицы в свою модель, вы получите два отношения «один ко многим», что означает, что код, который вы пишете, и модель, которую вы имеете, будут готовы к любому количеству дополнительных столбцов полезной нагрузки, которые появляются по мере развития проекта.Стоимость дополнительного столбца целочисленных идентификаторов, как правило, довольно мала, чтобы платить за гибкость модели.

(Из главы 2. Основы моделирования сущностных данных, 2.4. Моделирование множества дляМногие отношения с полезной нагрузкой)

Звучит как хороший совет.Тем более, что у вас уже есть полезная нагрузка (SKU).

2 голосов
/ 03 июля 2013

Я просто хотел бы добавить следующее к ответу Самуила:

Если вы хотите напрямую запросить с одной стороны отношения многие ко многим (с полезной нагрузкой) к другой, вы можете использоватьследующий код (с использованием того же примера):

Company c = context.Companies.First();
IQueryable<Product> products = c.Company_Products.Select(cp => cp.Product);

Переменная products будет тогда всеми Product записями, связанными с Company c записью.Если вы хотите включить SKU для каждого из продуктов, вы можете использовать анонимный класс, например:

var productsWithSKU = c.Company_Products.Select(cp => new {
  ProductID = cp.Product.ID,
  Name = cp.Product.Name,
  Price = cp.Product.Price,
  SKU = cp.SKU
});
foreach (var 

Вы можете инкапсулировать первый запрос в свойство только для чтения для простоты, например:

public partial class Company
{
  public property IQueryable<Product> Products
  {
    get { return Company_Products.Select(cp => cp.Product); }
  }
}

Вы не можете сделать это с запросом, который включает SKU, потому что вы не можете возвращать анонимные типы.Вы должны иметь определенный класс, что обычно делается либо путем добавления не сопоставленного свойства к классу Product, либо путем создания другого класса, который наследуется от Product, который будет добавлять свойство SKU.Однако если вы используете унаследованный класс, вы не сможете вносить в него изменения и управлять им с помощью EF - это будет полезно только для целей отображения.

Приветствия.:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...