Наследование с помощью EF Code First - Таблица для конкретного типа (TPC) - PullRequest
0 голосов
/ 02 января 2019

Я создал такой вид Таблица для конкретного типа (TPC) структура, как показано ниже:

enter image description here

Вотсущности, используемые здесь:

public abstract class BaseModel : MyOtherBaseClass
{
    [Key]
    public int Id { get; set; }

    //Foreign key for Project
    public int ProjectId { get; set; }

    public int Sequence { get; set; }

    public string Name { get; set; }

    public string IconUrl { get; set; }

    //Navigation Properties ####################
    public virtual Project Project { get; set; }
}


[Table("Tool")]
public class Tool : BaseModel
{
    [Key]
    public int Id { get; set; }

    public string ToolBrand { get; set; }

    //Navigation Properties ####################
    //public virtual Project Project { get; set; }
}


[Table("Priority")]
public class Priority : BaseModel
{
    [Key]
    public int Id { get; set; }

    public string PriorityCode { get; set; }

    //Navigation Properties ####################
    //public virtual Project Project { get; set; }
}

На данный момент я запутался в следующих вопросах:

1) Могу ли я использовать Id для сущностей Tool и Priority?И будет ли это абсолютно ненужным?

2) Я использую FK (ProjectId) и связанную таблицу Project в классе BaseModel.Но как столбцы ProjectId будут создаваться в таблицах инструментов и приоритетов, как можно создать отношение?Могут ли быть какие-либо проблемы?

3) Если я не предпочитаю Fluent API, я должен добавить в контекст сущности Tool и Priority, кроме сущности BaseModel?Потому что в некоторых ресурсах дочерние классы добавляются, а в других нет.Какой из них соответствует действительности?

public class EntityContext : DbContext
{
    public DbSet<BaseModel> BaseModel { get; set; }
    // ? public DbSet<Tool> Tool { get; set; }
    // ? public DbSet<Priority> Priority { get; set; }
}

Если есть какие-либо проблемы, связанные с этим использованием, не могли бы вы также сообщить мне?Спасибо ...

1 Ответ

0 голосов
/ 02 января 2019

Всякий раз, когда вы разрабатываете классы, вы должны знать, что классы действительно представляют.

Ваш DbSet<...> представляет одну таблицу вашей базы данных. Класс внутри DbSet представляет столбцы этой таблицы как не виртуальные свойства, а отношения между таблицами - как виртуальные свойства.

Мне кажется, вам нужны две таблицы в вашей базе данных: таблица с Tools и таблица с Priorities. В настоящее время вы думаете, что некоторые столбцы Tools также находятся в Priorities. Поэтому вы намереваетесь создать общий базовый класс, имя которого у вас не лучше, чем BaseModel.

Тот факт, что вы не можете придумать правильное имя для общего базового класса, должен предупредить вас, что, возможно, между этими двумя нет ничего общего. Вы уверены, что если вы описываете Tool, то это не случай, когда у него есть некоторые свойства с тем же именем и типом, что и у Priority

Например. Если бы вы определили строки Tools и строки Priorities, вы бы сказали, что каждый Tool используется в Project, а каждый Priority является приоритетом Project. Это часть дизайна вашей системы. По вашему определению бессмысленно иметь Tools без Project

Несмотря на это, согласно вашему определению, у каждого инструмента должен быть «метод уникальной идентификации», вы решили использовать для этого целочисленный идентификатор. Точно так же вы решили иметь целочисленный идентификатор для приоритетов. Однако для строки в вашей таблице Tool присуще то, что тип идентификации равен типу идентификации Приоритета? Хотели бы вы, чтобы ваш дизайн был бесполезным, если бы кто-то сказал вам, что у Tool есть Guid Id и у Priority есть целочисленный Id?

Нет, конечно, нет: ваш дизайн должен быть настолько надежным, чтобы небольшие изменения в таблицах вашей базы данных приводили к небольшим изменениям в вашем дизайне!

Какие свойства должны быть в базовом классе

1) Могу ли я использовать идентификатор для инструмента и Приоритета?

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

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

Конечно, это также относится ко всем другим свойствам: если они идентичны "по совпадению", поместите значения в производный класс. Если это типичное что-то общее между Tools и Priorities, поместите это в базовый класс.

Мне нелегко обнаружить сходство между инструментами проекта и приоритетами проекта, поэтому я перейду к другому примеру.

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

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

Где поставить внешний ключ?

В соответствии с вашим дизайном, Инструменты и Приоритеты - это вещи, принадлежащие Проекту. Если после предыдущего шага вы решили, что это единственное, что у них общего, то вы редко будете брать кучу объектов, которые содержат как инструменты, так и приоритеты.

В школьной базе данных было бы вполне нормальным объединить учеников и учителей в группу лиц, где у каждого человека был бы адрес, а в каждом адресе было бы проживать ноль или более человек (один-ко-многим)

// this will be a row in a table, hence it has an Id
class Address
{
     public int Id {get; set;}
     public string ZipCode {get; set;}
     public string Street {get; set;}
     ...

     // on every address live zero or more Persons (one-to-many)
     public virtual ICollection <Person> Persons {get; set;}
}

// this will not be a row in a separate table, hence it has no ID
class Person
{
     public string Name {get; set;}
     public DateTime Birthday {get; set;}
     ...
     // every Person lives at an Address, using foreign key
     public int AddressId {get; set;}
     public virtual Address Address {get; set;}
}

class Teacher : Person
{
    public int Id {get; set;}
    ...
}
class Student: Person
{
    public int Id {get; set;}
    ...
}

Таким образом, у вас будет три таблицы: адреса, учителя, студенты.И Учителя, и Студенты будут обладать свойствами Человека.Они оба живут по одному адресу.

Посмотрите, как мало нужно внести изменения, если мы решим добавить колонку в Учителя или Человека?Как мало нужно изменить, если вы хотите, чтобы идентификатор учителя был GUID или Address.Id был строкой (ожидайте изменений в первичном ключе адреса и внешнем ключе внутри Person).Посмотрите, как мало нужно изменить, если вы хотите добавить новый тип Person: Parent?

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

3, следует ли мне добавлять объекты в контекст помимо объекта BaseModel?

Помните?: каждый DbSet в вашем DbContext станет таблицей.Если вы не укажете Инструменты и Приоритеты как отдельные таблицы, вы не будете использовать Таблица на класс бетона (TPC) , но Таблица на иерархию (TPH) : как Инструменты, так и Приоритетыбудет в одной таблице

Я редко использую TPH из-за всех неиспользуемых нулевых значений, которые я получаю в своих таблицах.

Если вы чаще всего спрашиваете «Учителя, которые ...» и «Ученики, которые ...», вы должны хранить их в отдельных таблицах.Столбцы базового класса также находятся в этих отдельных таблицах.

Если вы чаще всего будете спрашивать «Лица, которые ...», где эти лица могут быть студентами или учителями, рассмотрите возможность использования таблицы длятип (TPT) : таблица Persons и таблица Teachers с внешним ключом к таблицам Persons, а также таблица учеников с внешним ключом.Все свойства базового класса находятся в таблице Persons.

Легко видеть, что если вы спросите "Persons that ...", TPT запросит только одну таблицу, тогда как для TPC вам потребуется запроситьТаблица учителей, а также таблица учеников и объединение результатов.

Однако, если вы попросите «Ученики, которые ...», TPT необходимо будет присоединиться к таблице «Персоны» и таблице «Студенты».TPC здесь быстрее: доступ только к одной таблице.

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