4

Рассмотрим следующие таблицы базы данных. К сожалению, таблицы не могут быть изменены каким-либо образом.EntityFramework Отношение сердечника к составному ключу

Database schema

Houses имеет поле автоматического приращения ID по имени Id, строковое поле с именем Name и поле целое с именем AreaId. Последний не является внешним ключом для таблицы Areas.

Areas имеет составной ключ, состоящий из AreaId, CountryId и LangId. Область с тем же AreaId может существовать, но с различными CountryId и LangId. Например: могут быть две строки с одинаковыми AreaId, но разные LangId.

ПРИМЕЧАНИЕ: Почему у House есть несколько Area? A House не имеет нескольких Area's, it only has one Площадь . The Таблица площади имеет составной ключ, что означает, что определенная строка будет иметь несколько переводов. Например, у идентификатора области 5 может быть LangId 5 для английского и LangId 3 для испанского языка.

Эти две таблицы описываются следующими двумя классами C#.

public class House 
{ 
    public int Id { get; set; } 

    [MaxLength(80)] 
    public string Name { get; set; } 

    public int? AreaId { get; set; } 

    [ForeignKey("AreaId")] 
    public List<Area> Areas { get; set; } 
} 

public class Area 
{ 
    public int AreaId { get; set; } 

    public int CountryId { get; set; } 

    public string LangId { get; set; } 

    public string Name { get; set; } 
} 

Совокупный ключ определен в контексте, как указано в документах.

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Area>() 
     .HasKey(a => new { a.AreaId, a.CountryId, a.LangId }); 
} 

Например, давайте получим список всех домов в базе данных, включая их соответствующие области.

_context.Houses.Include(h => h.Areas).ToList(); 

Следующий SQL-генерируется в окне вывода и полученный список содержит Дома неправильно подобранные с областями.

SELECT [a].[AreaId], [a].[CountryId], [a].[LangId], [a].[Name] 
FROM [Areas] AS [a] 
WHERE EXISTS (
    SELECT 1 
    FROM [Houses] AS [h] 
    WHERE [a].[AreaId] = [h].[Id]) 
ORDER BY [a].[Id] 

Как вы можете видеть, EntityFramework относится [a].[AreaId] с [h].[Id] и не [h].[AreaId]. Как я могу выразить эти отношения в EF?

+0

Вы можете показать код конфигурации на комбинированном ключе? – Sampath

+0

Я редактировал вопрос, добавляя конфигурацию составного ключа – giannoug

+0

Я не думаю, что это возможно. Довольно странный дизайн db, даже нелогичный - как один дом находится в нескольких областях и т. Д. Во всяком случае. –

ответ

9

В EF вы не сможете точно установить карту. Если вы хотите, чтобы House ссылался на Area, внешний ключ должен состоять из тех же полей, что и составной ключ Area, иначе EF не примет сопоставление. Обход может состоять в том, чтобы пропускать отображение и присоединяться к объектам вручную, когда это необходимо, но это скрывает реальную проблему: плохой дизайн.

Основная ошибка дизайна заключается в том, что при добавлении переводов необходимо дублировать Area. Теперь вопрос - и всегда будет - какая запись представляет мой физический объект Area? Основной предпосылкой реляционной базы данных является то, что объекты представлены уникальными записями. Ваш дизайн нарушает этот основной принцип.

К сожалению, таблицы не могут быть изменены каким-либо образом.

Ну, они должны be! Оставлять его таким образом нельзя даже рассматривать.Вы не должны работать с извращенной реляционной моделью, это слишком важно для плавной разработки приложений.

модель, как я могу собрать вместе из вашего описания, следует, вероятно, что-то вроде этого:

public class House 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int? AreaId { get; set; } 
    public Area Area { get; set; } 
} 

public class Area 
{ 
    public int Id { get; set; } 
    public int CountryId { get; set; } 
    public Country Country { get; set; } 
    public string Name { get; set; } // E.g. the name in a default language 
    public ICollection<AreaTranslation> AreaTranslations { get; set; } 
} 

public class AreaTranslation 
{ 
    public int AreaId { get; set; } 
    public int LanguageId { get; set; } 
    public string LocalizedName { get; set; } 
} 

public class Country 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Language 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Для этой модели требуется один явные инструкции отображения (EF будет выводить остаток):

modelBuilder.Entity<AreaTranslation>() 
      .HasKey(a => new { a.AreaId, a.LanguageId }); 

Вы видите, что Area теперь искренне представляет физическую зону. A House теперь, естественно, имеет один Area, а не эту странную коллекцию Area s, которую нужно как-то рассматривать как одну область. Различные языки входят в класс AreaTranslation. Я предполагаю, что Area принадлежит к одному Country.

+1

Спасибо за подробный ответ! Я унаследовал базу данных, это не мой дизайн (я не архитектор базы данных, но я могу использовать здравый смысл). Вы дали мне твердые точки, чтобы укрепить мою позицию по новой схеме – giannoug

Смежные вопросы