Работаем с SQLite в C# посредством объектного слоя. Часть 1

Доброго времени суток! В данной статье мы начнем расcматривать с Вами работу с базой данных SQLite в C#.
Мы реализуем свою собственную объектную обертку для работы с базой, по аналогии с тем как это сделано
в промышленных фреймворках, т.е. мы будем с базой работать посредством объектов и результат выборки из
базы также будет представлен объектами или их списками.

SQLite — это переносимая база данных, которая имеет небольшой размер и позволяет сохранять структурированные
данные и работать с ними посредством знакомых SQL-команд.

Фактически у нас будут обычные объекты C#, которые мы будем сохранять, извлекать, удалять и модифицировать
без работы с SQL-командами напрямую.

Итак, приступим.

Файл Program.cs


using Microsoft.Data.Sqlite; // эту библиотеку для работы с SQLite берем из пакетного менеджера nuget
using System.Data;
using System.Reflection;
using System.Text;

namespace CSharpSqliteDemo
{   
    // наш собственный атрибут для ID полей классов-сущностей
    // говорит о том, что это поле автоматические генерируется базой
    [AttributeUsage(AttributeTargets.Property)]
    class AutoGeneratedAttribute : Attribute { }

    // базовый класс сущности базы данных
    // в нашем случае нужен для форматирования объекта в строку
    class Entity
    {

        public override string ToString()
        {
            var className = GetType().Name;
            var properties = GetType().GetProperties();
            var list = new List<string>();

            foreach (var property in properties)
            {
                list.Add($"{property.Name}={property.GetValue(this)}");
            }

            return $"[Object {className}({string.Join(';', list)})]";
        }
    }

    // представляет пользователя из таблицы Users в базе данных
    class User : Entity
    {
        // это поле генерируется автоматически базой данных
        [AutoGenerated]
        public int Id { get; set; }

        // имя 
        public string? Name { get; set; }

        // возраст
        public int Age { get; set; }

    }

    // точка входа в приложение
    internal class Program
    {
        static void Main(string[] args)
        {
            // создаем подключение к БД пользователей, если файла БД не существует он будет создан автоматически
            using var connection = new SqliteConnection("Data Source=usersdata.db");

            // класс репозитория пользователя - работает с базой данных
            // посредство операций чтения, изменения, удаления и обновления
            using var repo = new UserRepository(connection);

            // создаем объект пользователя, который отображается на строки таблицы (конвертируется в строки таблицы БД)
            var user = new User
            {
                Name = "Михаил Русаков",
                Age = 30
            };

            // создаем пользователя и возвращаем его идентификатор (который создала для нас база)
            int lastId = repo.Create(user);

            // читаем данные из таблицы и возвращаем объект пользователя
            var dbUser = repo.Read(19);

            Console.WriteLine(dbUser);

            // обновление записи в базе данных
            repo.Update(new User
            {
                Id = 1,
                Name = "Misha",
                Age = 29
            });

            // удаляет пользователя с Id = 1
            repo.Delete(1);
        }
    }
}

Файл UserRepository.cs


using Microsoft.Data.Sqlite;

namespace CSharpSqliteDemo
{
    // класс репозитория пользователей расширяет обобщенный базовый класс 
    class UserRepository : BaseRepository<User>
    {
        public UserRepository(SqliteConnection dbConnection) : base(dbConnection, "Users") {}

        // здесь мы в ручную создаем структуру нашей таблицы (ее поля должны соответствовать полям класса)
        protected override string TableSchemaSql()
        {
            return $@"
                CREATE TABLE IF NOT EXISTS {table} (
                    Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, -- идентификатор пользователя (автоматически генерируется базой)
                    Name TEXT NOT NULL,                                   -- имя пользователя (строка, не может быть пустой)
                    Age INTEGER NOT NULL                                  -- возраст (число, не может быть пустым)
            )";
        }

    }
}

Таким образом, в данной статье мы рассмотрели объектный интерфейс для работы с базой данных SQLite, а в следующей рассмотрим
непосредственно саму реализацию этого интерфейса.

Источник