Найти в Дзене
ZZnOB.Ru

SQLite-Net Extensions один к многим

Оглавление

В третьем посте из серии SQLite-Net Extensions мы рассматриваем последний тип отношений – один-ко-многим (и наоборот – многие-к-одному).

Один ко многим, многие к одному

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

Отношение «один ко многим» означает, что объект объект знает о своих дочерних объектах, а ссылающиеся объекты  имеют ссылку (внешний ключ) на своего родителя (но не обязательно знают об этом).

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

Я использовал глагол “знать” несколько раз, так что пришло время объяснить что я имею в виду. «Зная» о другом объекте отношений, я понимаю, что имею в виду ссылку на него. Это означает, что, например, в отношениях «многие к одному» один объект не имеет отношения к своим дочерним.

Однако в большинстве случаев нам хотелось бы иметь гибрид отношений один-ко-многим и многие-к-одному. Я назову это один ко многим с инверсией. Мы хотим, чтобы оба родителя знали о своих детях, и каждый ребенок знал о своих родителях.

В этом посте мы рассмотрим один-ко-многим без реверсии и один-ко-многим с реверсией, так как он также включает отношения многие-к-одному, чтобы вы могли получить исчерпывающий обзор. Мы увидим это на примере сущностей Employee и Duty *. Каждый сотрудник имеет список своих обязанностей, в то время как каждая отдельная обязанность назначается только одному сотруднику.

Один ко многим без инверсии

-2

Во-первых, давайте смоделируем такие отношения. Теперь мы можем преобразовать его в классы C #:

[Table("Employees")]
public class Employee
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public string Name { get; set; }
public string LastName { get; set; }

[OneToMany]
public List<Duty> Duties { get; set; }
}

В классе Employee (родитель, один объект отношения) мы определяем коллекцию дочерних элементов, украшенную атрибутом OneToManyAttribute. Типы коллекций, поддерживаемые на момент написания этой статьи SQLite-Net Extensions, это List и Array и могут использоваться по вашему усмотрению.

Давайте теперь посмотрим, как выглядит дочерняя сущность (Duty):

[Table("Duties")]
public class Duty
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public string Description { get; set; }

public DateTime Deadline { get; set; }

[ForeignKey(typeof(Employee))]
public int EmployeeId { get; set; }
}

В классе Duty (дочерний, многоконечные отношения) нам нужно определить внешний ключ для родительской сущности. Для этого мы создаем свойство, представляющее его (EmployeeId), декорируя его с помощью ForeignKeyAttribute, дополнительно указывая тип родительской ссылки (Employee).

Вот и все. Мы уже можем использовать его в нашем коде:

var db = new SQLiteConnection(new SQLitePlatformAndroid(), Constants.DbFilePath);
db.CreateTable<Employee>();
db.CreateTable<Duty>();

var employee = new Employee
{
Name = "Andrew",
LastName = "Programmer"
};

var duty1 = new Duty()
{
Description = "Project A Management",
Deadline = new DateTime(2017, 10, 31)
};

var duty2 = new Duty()
{
Description = "Reporting work time",
Deadline = new DateTime(2022, 12, 31)
};

db.Insert(employee);
db.Insert(duty1);
db.Insert(duty2);

employee.Duties = new List<Duty> {duty1, duty2};
db.UpdateWithChildren(employee);

var employeeStored = db.GetWithChildren<Employee>(employee.Id);

Здесь нет ничего сложного. Что нас интересует, так это то, как employeeStored выглядит в итоге:

-3

Как видите, метод GetWithChildren возвратил объект типа Employee с надлежащим образом извлеченной коллекцией Duties (содержащей две обязанности, назначенные ранее сотруднику). Более того, у каждого дочернего элемента есть свой внешний ключ (EmployeeId), автоматически извлекаемый из БД – здесь нет никаких дополнительных затрат, это просто поле внешнего ключа, хранящееся в той же таблице базы данных SQLite (Duties).

Один-ко-многим с инверсией

(один-ко-многим + многие-к-одному)

Как и ранее, давайте сначала посмотрим, как меняется диаграмма классов после добавления инверсии:

-4

Что изменилось, так это то, что теперь у каждого Duty есть свойство типа Employee.

Чтобы реализовать вышеприведенную диаграмму классов и сделать так, чтобы каждый дочерний объект (в нашем случае, каждый Duty) знал о своем родителе (имея ссылку на ответственного сотрудника), нам нужно только добавить следующее свойство в класс модели Duty:

[ManyToOne]
public Employee Employee { get; set; }

поэтому класс модели в итоге выглядит следующим образом:

[Table("Duties")]
public class Duty
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

public string Description { get; set; }

public DateTime Deadline { get; set; }

[ForeignKey(typeof(Employee))]
public int EmployeeId { get; set; }

[ManyToOne]
public Employee Employee { get; set; }
}

Как вы можете видеть, мы только что создали отношение многие-к-одному, используя ManyToOneAttribute. Таким образом, в настоящее время у нас есть гибрид обоих типов отношений в рамках двух моделей.

Вызовы в нашем коде не нужно менять вообще. Теперь сущность employeeStored после инициализации тем же методом GetWithChildren, что и ранее, для каждой обязанности в дополнение к полю EmployeeId также содержит свойство Employee, надлежащим образом извлеченное SQLite-Net Extensions:

-5

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

Резюме

Сегодня мы увидели, как моделировать и использовать отношения «один ко многим» (с инверсией и без нее) в базе данных SQLite с использованием расширений SQLite-Net. Автоматическая инициализация односторонних или многоцелевых объектов с помощью ORM чрезвычайно полезна при работе с такими объектами в нашем приложении. Количество кода для написания также минимально.

Не забудьте зайти на блог, там бывают полезные записи, которые я не публикую тут!

А чтобы быть в курсе последних моих постов, подписывайтесь на мой канал в Telegram.