Политипные ассоциации

Я собираюсь использовать Entity Framework в ближайшее время для системы бронирования (сделанной с нуля).

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

Рассмотрим этот случай:

У меня есть Бронирование, и у бронировки могут быть связанные Ressources, которые могут быть забронированы, но эти ресурсы могут быть разными и иметь разные поля и т.д.

ER Diagram of the Relationship

Я никогда раньше не использовал EF, поэтому я не знаю, как я могу добиться такого рода полиморфизма, который я использую в других проектах (где мы использовали исходный SQL).

Модель С# будет выглядеть так:

public class Booking
{
    public int BookingID { get; set; }
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
    public IRessource Ressources { get; set; }
    ...
}

public interface IRessource
{
    public int RessourceTypeId { get; set; }
}

public class Room : IRessource
{
    public int RoomID { get; set; }
    public int RessourceTypeId { get; set; }
    ...
}

public class Car : IRessource
{
    public int CarID { get; set; }
    public int RessourceTypeId { get; set; }
    ...
}

Является ли это достижимым, и если да, то как? Как будет выглядеть запрос?

Ответ 1

Да, абсолютно.

Похоже, вам действительно нужна связь Table Per Type (TPT). Это соответствует стандартным отношениям внешних ключей is-a / has-a. Обратите внимание, что по умолчанию Entity Framework использует таблицу для иерархии (TPH).

Вот несколько ссылок:

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

modelBuilder.Entity<IRessource>().ToTable("IRessources");
modelBuilder.Entity<Room>().ToTable("Rooms");
modelBuilder.Entity<Car>().ToTable("Cars");

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

Что-то вроде: SELECT * FROM IRessource i JOIN Room r ON i.id == r.id

Однако вы часто обнаружите, что если вы используете контроллер Web API, если вы просто вытащите весь Room POCO, вы получите все его свойства сразу без соединения вообще!


По моему опыту, я также обнаружил, что обычно ситуация становится более гладкой, если вы не используете интерфейсы или абстрактные классы, потому что если вы это сделаете, вам придется сделать объекты передачи данных (DTO) для передачи данных изредка, поскольку вы можете создавать объекты этих типов. Вещи могут стать немного грязными - особенно если вы не уверены, что именно вы делаете. Чтобы помочь вам понять, подумайте о контроллере, с которым вы хотите перейти в родовом IRressource, он попытается преобразовать эти объекты, поскольку он получает их в один, который не может быть создан. Вот досада! Таким образом, простое решение состоит в том, чтобы сделать как базовый класс, так и унаследованные классы нормальными классами.