Настройте отношение «один-один-один / один» с несколькими таблицами, используя сущность - PullRequest
0 голосов
/ 29 мая 2018

Я нахожусь в ситуации, когда одна таблица имеет два отношения "один-один / один".Как мне реализовать это, используя Entity Framework Code-First?

Я видел следующие ссылки

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

Я также видел Настройка множественных связей от 1 до 0..1 между таблицами сущностей , которые смутили меня до неузнаваемости.

См. Ниже соответствующую часть моегоДиаграмма БД: Так что, по сути, Player не следует сохранять без DKImage, аналогично Product не следует сохранять без DKImage.

Ниже приведен код для моделей: Players, Products, DKImages ( Я знаю, что это неправильно, я реализовал его только таким образом, чтобы я мог сгенерировать базу данных и показать диаграмму )

Player

public enum Positions { PG, SG, SF, PF, C }

public class Player
{
    [Key]
    [ForeignKey("Images")]
    public int PlayerID { get; set; }

    [Required]
    public string PlayerName { get; set; }

    [Required]
    public string PlayerLastName { get; set; }

    [Required]
    public int PlayerAge { get; set; }

    [Required]
    public Positions Position { get; set; }

    [Required]
    public bool Starter { get; set; }

    [Required]
    [Display(Name = "Active / Not Active")]
    public bool Status { get; set; }

    //Foreign Keys
    public int PlayerStatsID { get; set; }

    //Navigation Properties
    [ForeignKey("PlayerStatsID")]
    public virtual IQueryable<PlayerStats> PlayerStats { get; set; }
    public virtual DKImages Images { get; set; }
}

DKImages

public class DKImages
{
    [Key]
    public int ImageID { get; set; }
    [Required]
    public string ImageURL { get; set; }
    [Required]
    public DateTime DateUploaded { get; set; }

    //Foreign Keys
    [Required]
    public int CategoryID { get; set; }

    //Navigation Properties
    public virtual Products Products { get; set; }
    public virtual Category Category { get; set; }
    public virtual Player Player { get; set; }
}

Продукты

public class Products
{
    [ForeignKey("Images")]
    [Key]
    public int ProductID { get; set; }
    [Required]
    public string ProductName { get; set; }
    [Required]
    public DateTime DateAdded { get; set; }

    //Foreign Keys
    [Required]
    public int ProductTypeID { get; set; }

    //Navigation Properties
    [ForeignKey("ProductTypeID")]
    public virtual ProductType ProductType { get; set; }
    public virtual DKImages Images { get; set; }
}

Редактировать

Мне сказали, что код выше верен.Если так, то как мне создать операторы CRUD LINQ (или любой другой метод построения операторов CRUD в этом отношении) с помощью приведенного выше кода.

Ответы [ 2 ]

0 голосов
/ 15 июня 2018

То, что вы хотите здесь, называется полиморфными ассоциациями : несколько сущностей, имеющих дочерние сущности одного типа.Они обычно используются для комментариев, замечаний, файлов и т. Д. И обычно применяются к ассоциациям 1: n.В вашем случае возникают полиморфные 1: 1 ассоциации.В основном эти ассоциации выглядят так (используя несколько более общие имена):

Как их реализовать?

Entity Framework 6

В EF6 это проблема.EF6 реализует связи 1: 1 как общие первичные ключи: первичный ключ дочернего элемента также является внешним ключом первичного ключа его родителя.Это означало бы, что на Image.ID должно быть два ФК, один указывает на Person.ID, а другой - на Product.ID.Технически это не проблема, семантически это так.Теперь двум родительским объектам принадлежит одно и то же изображение или 1020 *, или, по-разному, изображение всегда должно принадлежать двум разным родителям.В реальной жизни это чепуха.

Решением может быть обратная ссылка:

enter image description here

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

Тем не менее, поскольку в вашем случае вы хотите, чтобы изображения требовались, позвольте мне показать, как эта связь отображается в EF6.

Давайте рассмотримэта простая модель:

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Image
{
    public int ImgID { get; set; } // Named for distinction
    public string Url { get; set; }
}

Требуемое отображение:

modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID);
modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent();
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();

Как видите, Image имеет двух обязательных зависимых.Возможно, это лучше, чем двое обязательных родителей, но все же странно.К счастью, в действительности это не проблема, потому что EF не проверяет эти ассоциации.Вы даже можете вставить изображение без «обязательной» зависимости.Я не знаю, почему EF не подтверждает это, но здесь это пригодится.Часть WithRequiredDependent могла бы также быть WithOptional, она не имеет значения для сгенерированной модели данных, но по крайней мере это отображение передает ваши намерения.

Альтернативный подход может быть наследование .Если Person и Product наследуются от одного базового класса, этот базовый класс может быть основным в ассоциации 1: 1 с Image.Тем не менее, я думаю, что это злоупотребление шаблоном дизайна.Люди и продукты не имеют ничего общего.С точки зрения дизайна, нет причин для того, чтобы они были частью одного дерева наследования.

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

Entity Framework Core

В EF-core 1: 1 ассоциации могут быть реализованы способом EF6, но также возможно использовать отдельное поле внешнего ключа в зависимом объекте.При этом полиморфный случай выглядит следующим образом:

Класс Image отличается:

public class Image
{
    public Image()
    { }
    public int ImgID { get; set; }
    public int? PersonID { get; set; }
    public int? ProductID { get; set; }
    public string Url { get; set; }
}

ИСопоставление:

modelBuilder.Entity<Person>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Person>()
    .HasOne(p => p.Image)
    .WithOne()
    .HasForeignKey<Image>(p => p.PersonID);
modelBuilder.Entity<Product>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Product>()
    .HasOne(p => p.Image)
    .WithOne()
    .HasForeignKey<Image>(p => p.ProductID);
modelBuilder.Entity<Image>().HasKey(p => p.ImgID);

Смотрите обнуляемые внешние ключи.Они необходимы, потому что изображение принадлежит либо Person, либо Product.Это один недостаток этого дизайна.Другое - то, что вам нужно новое поле внешнего ключа для каждого нового объекта, которому вы хотите владеть изображениями.Обычно вы хотите избежать таких редких столбцов.Есть также преимущество по сравнению с реализацией EF6: эта модель допускает двунаправленную навигацию.Image может быть расширен с помощью Person и Product навигационных свойств.

EF довольно неплохо работает, переводя это в дизайн базы данных.Каждый внешний ключ имеет отфильтрованный уникальный индекс , например, для Person:

CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
    [PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)

Это превращает ассоциацию в подлинную ассоциацию 1: 1 на стороне базы данных.Без уникального индекса это была бы связь 1: n с точки зрения базы данных.

0 голосов
/ 29 мая 2018

Пример в вашей таблице Игрока будет следующим:

public class Player
{

    // All the rest you already coded

    [Required]
    public int ImageID

    [ForeignKey("ImageID")]
    public virtual DKImage DKImage {get;set;}
}

Это заставит игрока иметь DKImage, но, как сказано в комментариях, это создаст отношения один ко многим.

Другим выходом было бы поместить все поля Player в таблицу DKImage, если бы к этому DKImage не было привязано ни одного игрока.

Изменить от 1 до 1..0

Ссылка Ивана Стоева получила довольно интересное представление о том, как этого добиться:

https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations

Похоже, вам придется добавить немного больше кодав вашем классе:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<DKImage>().HasOptional(t => t.Player).WithRequired();

}

Если учебное пособие верно, это будет выглядеть следующим образом:

«Сущность DKImage имеет необязательную связь с одним объектом Player, но эта связь требуется для сущности Player».

Я еще не проверял.

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