В сегодняшнем посте мы рассмотрим, что такое SQLite-Net Extensions ORM и как его использовать для создания отношений «многие ко многим» в базе данных SQLite. Другие типы отношений будут описаны в отдельных постах.
Что такое SQLite-Net Extensions?
Поскольку вы разрабатываете приложение, рано или поздно вам нужно будет хранить данные вашего приложения в некотором постоянном хранилище. В своем проекте я выбрал базу данных SQLite, используя библиотеку SQLite.NET для выполнения операций над ней. На самом деле это очень легкая и простая в использовании структура базы данных, но недавно я понял, что мне нужно смоделировать некоторые отношения в моей базе данных. SQLite не предлагает никаких полезных утилит для моделирования таких отношений.
Однако, если вам нужно смоделировать какие-либо отношения в вашей базе, в SQLite.NET есть обертка, которая позволяет это делать — это SQLite-Net Extensions. Он в основном расширяет основные функциональные возможности SQLite.NET, добавляя элементы, которые позволяют легко обрабатывать отношения, включая один-к-одному, один-ко-многим, многие-к-одному и многие-ко-многим.
В этом посте мы увидим, как создать отношение «многие ко многим» с помощью этой библиотеки. Мне нужны были такие отношения, чтобы смоделировать связь между сущностями Person и Event в моем приложении.
Отношения «многие ко многим»
Давайте посмотрим на отношение многие ко многим на примере двух сущностей: Персона и Событие. Событие (сущность типа Event) может содержать ноль или более участников (сущности типа Person), в то время как персона может быть назначена на ноль или более событий. Это типичное отношение «многие ко многим», которое мы собираемся установить в нашей базе данных сейчас.
Устанавливаем SQLite-Net Extensions
Если вы ранее использовали SQLite.NET в своем проекте — сначала удалите его. Я не делал этого до того, как начал использовать SQLite-Net Extensions, и у меня много проблем с тем, что Visual Studio неправильно обрабатывала мои ссылки. SQLite-Net Extensions — это оболочка для SQLite.NET, поэтому она уже содержит эту библиотеку и дополнительно расширяет ее, добавляя некоторые дополнительные функции для обработки отношений.
SQLite-Net Extensions могут быть установлены как пакет Nuget в ваше решение. В зависимости от версии, которую вы хотите использовать, выполните соответствующую команду в консоли диспетчера пакетов в Visual Studio:
синхронный:
Install-Package SQLiteNetExtensions -Version 2.1.0
асинхронный:
Install-Package SQLiteNetExtensions.Async -Version 2.1.0
Определяем модельные классы
Затем нам нужно определить классы модели Person и Event и установить отношения между ними. Ниже вы можете найти код обоих классов:
// Person class modelling People table
[Table("People")]
public class Person
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
[ManyToMany(typeof(PersonEvent))]
public List<Event> Events { get; set; }
}
// Event class modelling Events table
[Table("Events")]
public class Event
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public string Place { get; set; }
[ManyToMany(typeof(PersonEvent))]
public List<Person> Participants { get; set; }
}
Как вы можете видеть, модели выглядят почти так же, как сущности БД SQLite.NET, со следующими исключениями:
- ManyToManyAttribute — на обоих объектах вы можете заметить, что этот атрибут определен. В классе модели Person я украшаю им коллекцию Events, тогда как в классе модели Event я украшаю им коллекцию участников.
- PersonEvent — вы могли заметить, что в качестве аргумента для атрибута ManyToManyAttribute в обеих моделях я передал тип PersonEvent. Как вы, возможно, знаете, при моделировании отношений «многие ко многим» нам нужна промежуточная сущность, чтобы хранить такие отношения в таблицах базы данных. Классический пример отношений студенты и курсы:
Нам также необходимо определить такую промежуточную сущность в нашем коде.
Реализация класса промежуточной модели PersonEvent выглядит следующим образом:
public class PersonEvent
{
[ForeignKey(typeof(Person))]
public int PersonId { get; set; }
[ForeignKey(typeof(Event))]
public int EventId { get; set; }
}
Благодаря атрибутам PrimaryKey, определенным для сущностей Person и Event, ORM сможет определить, к каким первичным ключам относятся внешние ключи в этой промежуточной таблице.
В атрибуте ManyToManyAttribute, кроме типа промежуточной сущности, вы можете установить CascadeOperations, который указывает, как следует обрабатывать каскад при работе с сущностями (например, следует ли выполнять операцию каскадного удаления при удалении одной из сторон отношения).
Вставка и чтение данных
Как только наши модельные классы определены, мы можем записывать и читать данные с отношением «многие ко многим». Следующий код представляет простой способ создать нового человека и назначить его событию:
var db = new SQLiteConnection(new SQLitePlatformAndroid(), Constants.DbFilePath);
db.CreateTable<Person>();
db.CreateTable<Event>();
db.CreateTable<PersonEvent>();
var event1 = new Event
{
Name = "Volleyball",
Date = new DateTime(2017, 06, 18),
Place = "Sports hall"
};
var person1 = new Person
{
Name = "A",
LastName = "B",
PhoneNumber = "123456789"
};
db.Insert(person1);
db.Insert(event1);
person1.Events = new List<Event> { event1 };
db.UpdateWithChildren(person1);
var personStored = db.GetWithChildren<Person>(person1.Id);
var eventStored = db.GetWithChildren<Event>(event1.Id);
Строки 1-4 содержат инициализацию базы данных и создание всех трех таблиц в базе данных.
Строки 6-18 — это просто создание объектов Персона и Событие, наполненных самыми основными деталями.
В строках 21 и 22 мы сначала сохраняем наши сущности person1 и event1 отдельно, потому что для установления отношений нам нужны первичные ключи тех сущностей, которые назначаются базой данных при сохранении. Это также можно упростить с помощью рекурсивных операций — более подробную информацию можно найти в официальной документации ORM.
После этого мы назначаем только что созданного человека на событие (строка 24), а затем наступает самая ракетостроительная часть:
db.UpdateWithChildren(person1);
Этот метод следующее: он обновляет человека со всеми его дочерними элементами (коллекция Events). Это сделает отношения установленными.
Чтобы доказать это, в строках 27 и 28 мы можем проверить, заполнены ли коллекции отношений дочерними элементами в обеих сущностях, вызвав методы расширения GetWithChildren:
Вот как работает SQLite-Net Extensions ORM. Он не обеспечивает ленивую загрузку связанных сущностей — он просто добавляет / извлекает в / из базы данных именно то, о чем вы говорите. Здесь существует ограничение: если вы обращаетесь к коллекции Person.Events, вы можете видеть события, с которыми связан этот человек, но если вы обращаетесь к Person.Events [0], вы не увидите всех людей, зарегистрированных для этого события.
Резюме
SQLite-Net Extensions — это ORM, который является оболочкой для классической библиотеки SQLite.NET. Он добавляет методы расширения / атрибуты для обработки отношений в базе данных SQLite. Он не предоставляет никакого механизма отложенной загрузки, вместо этого он предоставляет методы для получения / сохранения сущностей вместе со своими дочерними элементами (связанными сущностями). Он легкий и довольно простой в реализации, поэтому для небольших решений, таких как мобильные приложения, я полностью предпочитаю писать и поддерживать запросы SQL непосредственно в C # для обработки отношений.
В следующих статьях из серии о SQLite-Net Extensions ORM я представлю вам другие типы отношений, которые предлагает ORM. Оставайтесь с нами 🙂
Не забудьте зайти на блог, там бывают полезные записи, которые я не публикую тут!
А чтобы быть в курсе последних моих постов, подписывайтесь на мой канал в Telegram.