Проблема со связью «многие ко многим» + наследование TPH в Entity Framework 6

c# entity-framework entity-framework-6 many-to-many table-per-hierarchy

Вопрос

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

Так что я

public abstract class SoftwareFirmware
{
    public long Id { get; private set; }
    public ICollection<DeviceType> DeviceTypes { get; private set; } 

    public SoftwareFirmware()
    {
        DeviceTypes=new HashSet<DeviceType>();
    }
}

а также

public class DeviceType
{
    public long Id { get; set; }
    public virtual ICollection<Firmware> AvailableFirmwareVerions { get; private set; }
    public virtual ICollection<Software> AvailableSoftwareVerions { get; private set; }

    public DeviceType()
    {
        AvailableFirmwareVerions = new HashSet<Firmware>();
        AvailableSoftwareVerions = new HashSet<Software>();
    }
}

который, как вы видите, определяет множество отношений. Я определил два класса, которые происходят из SoftwareFirmware , метко названного

public class Firmware : SoftwareFirmware {}

а также

public class Software : SoftwareFirmware {}

Я использую наследование «Наследование на иерархию», поэтому Software и Firmware хранятся в одной таблице с столбцом дискриминатора. Наконец, я сопоставил отношения в DbContext от OnModelCreating используя

modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany(firmware=>firmware.DeviceTypes);
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany(sofware=>sofware.DeviceTypes);

Проблема в том, что Entity Framework, похоже, не поддерживает наследование с этим сопоставлением, поскольку я получаю следующее, когда EF пытается создать базу данных:

DeviceTypes: FromRole: «DeviceTypes» NavigationProperty недопустим. Тип «Программное обеспечение» FromRole «DeviceType_AvailableSoftwareVerions_Target» в AssociationType «DeviceType_AvailableSoftwareVerions» должен точно соответствовать типу «SoftwareFirmware», на котором объявлен этот NavigationProperty.

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

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


UPDATE: так кажется, что SQL Server Management Studio не поступила неправильно, поскольку я ранее планировал базу данных без перегруженной версии WithMany, которая принимает выражение и не включает таблицы переходов. Кажется, что SSMS не очень хорошо сочетается с изменениями схемы в терминах добавления новых диаграмм, даже когда база данных была удалена и воссоздана - ее необходимо перезапустить. Основная боль, но я отвлекаюсь ...

Как последнее усилие, я вернулся к WithMany версии WithMany для сопоставлений, удалил и воссоздал базу данных, перезапустив приложение, перезапустил SSMS и lo! Таблицы соединений были созданы. Все , что мне нужно сделать , это добавить Ignore для базового SoftwareFirmware класса DeviceTypes собственности и всего генерируемой чисто. Поэтому мой код отображения FluentAPI выглядит так:

modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany();
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany();
modelBuilder.Entity<SoftwareFirmware>().Ignore(s => s.DeviceTypes);

который генерирует эту схему - в значительной степени именно ту схему, которую я хотел (игнорировать дополнительные свойства):

введите описание изображения здесь

Однако, поскольку WithMany вызов WithMany только WithMany свойство навигации с одной стороны, обновления для Software.DeviceTypes и Firmware.DeviceTypes не отслеживаются EF, поэтому я вернулся туда, где я начал.

Принятый ответ

Проблема в том, что у вас есть одно свойство SoftwareFirmware.DeviceTypes, но вы затем пытаетесь использовать его как часть двух отдельных отношений. SoftwareFirmware.DeviceTypes не может быть обратным как для DeviceType.AvailableFirmwareVerions, так и для DeviceType.AvailableSoftwareVerions.

То, что вы пытаетесь моделировать, немного странно, потому что вы относитесь к ним как к разным отношениям, но также и к нет. Здесь есть два варианта ...

Вариант 1: это два отдельных отношения

Удалите SoftwareFirmware.DeviceTypes и добавьте свойство DeviceTypes в прошивку и программное обеспечение.

На самом деле это то, что вы делаете, когда вы помещаете Ignore в свойство SoftwareFirmware.DeviceTypes и используете пустую перегрузку WithMany - вот почему она работает. Вы говорите EF, что есть два отношения (одно программное обеспечение -> DeviceType и другое Firmware -> DeviceType) и что нет свойства навигации, которое указывает другой путь. Поскольку вы проигнорировали SoftwareFirmware.DeviceTypes, это просто не часть вашей модели.

Вариант 2: это одно отношение

Удалите два свойства навигации в DeviceType и замените их одной навигацией на базовый класс SoftwareFirmware. Вы всегда можете добавить некоторые свойства фасада, которые фильтруют содержимое для Программного обеспечения и прошивки (как показано ниже)

public class DeviceType
{
    public long Id { get; set; }

    public virtual ICollection<SoftwareFirmware> AvailableVerions { get; private set; }

    public virtual IEnumerable<Firmware> AvailableFirmwareVerions
    {
        get
        {
            return this.AvailableVerions.OfType<Firmware>();
        }
    }

    public virtual IEnumerable<Software> AvailableSoftwareVerions
    {
        get
        {
            return this.AvailableVerions.OfType<Software>();
        }
    }

    public DeviceType()
    {
        AvailableVerions = new HashSet<SoftwareFirmware>();
    }
}

Популярные ответы

Эта проблема звучит знакомо. (проверяя мою электронную почту ... да, это было более года назад!) Я попросил кого-нибудь отправить мне образец, в котором проваливались быстрые отношения апи. У них не было много ко многим, но я думаю, что это та же проблема. Я долгое время смотрел на него и спросил Роуэн Миллер (в команде), и он сказал, что беглый апи не может понять свойство, исходящее из базового типа.

т.е. свободный API не может видеть свойство DEVICETYPE, когда он смотрит на AvailableSoftwareVerions или на AvailableFirmwareVersions. (Я не могу сказать вам, ПОЧЕМУ это так. Вы думаете, что это может найти это через отражение, но, возможно, это просто не было разработано с учетом этого сценария.)

Это все еще не имело смысла для меня, поэтому он объяснил далее (и я обновляю его объяснения с вашими типами, которые были немного запутанными, так как у вас есть дополнительные уровни наследования, и вещи называются немного непоследовательно ... но я

Понятно, что классы действительно не имеют смысла, потому что DeviceType может иметь много Программных (ых) или Прошивок (ы) â € |, но свойство обратной навигации определено в SoftwareFirmware. Итак, что происходит, когда что-то, что не является прошивкой или программным обеспечением, имеет DeviceType? Его инверсия настроена как> DeviceType.AvailableSoftwareVersions, но это не может работать. Даже принимая EF из изображения, правильный способ моделирования, чтобы иметь свойство Project в отчете.

Это было с EF5. Если моя память правильная, и это та же проблема, возможно, она не изменилась для EF6. Возможно, нам стоит посмотреть, есть ли проблема для решения этой проблемы. Однако его дальнейшее объяснение говорит о том, что это не ошибка, а защита.

(Я собираюсь проверить его, чтобы убедиться, что я правильно сформулировал предыдущую проблему).

В этом письме Роуэн также предложил использовать геттерную логику вместо свойств навигации в качестве обходного пути.




Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему