cs_mysql_connection3.md 18 KB

Создание подключения к БД MySQL.

Дальше мы продолжим разбор задания одного из прошлых демо-экзаменов.

Базу мы развернули и данные в неё импортировали, теперь начнём разбор второй сессии: создание desktop-приложения.

Разработка desktop-приложений

Список продукции

Необходимо реализовать вывод продукции, которая хранится в базе данных, согласно предоставленному макету (файл product_list_layout.jpg находится в ресурсах). При отсутствии изображения необходимо вывести картинку-заглушку из ресурсов (picture.png).

...

Стоимость продукта должна быть рассчитана исходя из используемых материалов.

По макету видно, что на первом экране уже нужны почти все данные, которые мы импортировали ранее: наименование продукта и артикул (таблица Product), тип продукта (ProductType), список материалов и стоимость материалов (Material через ProductMaterial).

Есть несколько вариантов работы с данными:

  • ORM Фреймворки (библиотеки) - предпочтительный вариант, его мы и будем дальше использовать.

  • Загрузака с помощью DataAdapter в наборы данных (DataSet). Для наборов данных можно даже установить связи между таблицами. Эта технология широко применялась в эпоху Windows Forms (этот способ рассматривался в старых версиях лекций)

  • Ещё один устаревший вариант лекций для WPF "заточен" на работу с объектами, в котором модели создавались вручную и данные в них грузились с помощью SQL-запросов и DataReader-a

ORM (Object-Relational Mapping, объектно-реляционное отображение) — технология программирования, суть которой заключается в создании «виртуальной объектной базы данных».

Благодаря этой технологии разработчики могут использовать язык программирования, с которым им удобно работать с базой данных, вместо написания операторов SQL или хранимых процедур. Это может значительно ускорить разработку приложений, особенно на начальном этапе. ORM также позволяет переключать приложение между различными реляционными базами данных. Например, приложение может быть переключено с MySQL на PostgreSQL с минимальными изменениями кода.

Ранее мы использовали .NET Framework + WPF, но с этого года перейдем на .NET Core + Avalonia.

Во-первых, .NET Framework работает только под ОС Windows и больше не поддерживается (последняя поддерживаемая версия C# - 4.7.2).

Во-вторых, ORM EntityFramewok не "дружит" с последними версиями MySQL коннектора

.NET Core - кроссплатформенная библиотека, поддерживает последние версии языка и ORM EntityFramewokCore нормально работает с MySQL.

WPF заменим на Avalonia, пока ограничимся ОС Windows, но в перспективе перейдём и на Linux/MacOS.

В качестве IDE будем использовать JetBarains Rider. Продует платный. Теоретически для неё есть бесплатные образовательные лицензии, но нам они недоступны. Активируйте ключём (гуглится по запросу "rider activation key", ключи действуют год и могут быть отозваны, поэтому конкретный ключ не указываю).

Установка Rider

Ссылка для скачивания Rider

Установка Avalonia на Rider

  1. Запустить собственно сам Rider

  2. В основном окне выбрать вкладку Configure -> Plugins

  3. Установить AvaloniaRider (в строке поиска введите "avalonia")

    При установке может выдать сообщение, что плагин разработан не в JetBrains и использовать на свой страх - соглашаемся (Accept)

  4. После установки плагина перезагрузите IDE и установите шаблоны проектов для Avalonia. В консоли выполните команду:

    dotnet new install Avalonia.Templates
    

    Для .NET 6.0 и более ранних версий замените install на --install. Версию .NET можно узнать выполнив в консоли команду dotnet --version

    Если шаблоны установлены нормально, то в окне создания нового проекта появится секция Other:

Создание проекта, подключение пакетов для работы с БД.

Итак, создадим проект на C# по шаблону Avalonia .NET Core App

В контекстном меню проекта выберите "Manage NuGet Packages"

  • Установите пакет "Microsoft.EntityFrameworkCore.Design"

    Версия пакета должна соответствовать версии .NET. У меня стоит 6-я версия, вам нужно смотреть командой dotnet --version

  • Установите пакет "MySql.EntityFrameworkCore"

    Версия пакета должна соответствовать версии .NET.

Создание подключения к БД (контекст).

Для подключения к базе данных нужно создать модели и контекст подключения (адрес сервера, название БД, логин и пароль). Тут есть два варианта:

  1. Application First (сначала приложение) - в этом варианте модели и контекст подключения создаются руками в приложении, а ORM автоматически создает по ним одноимённые сущности в БД. (пример такого подхода расписан в метаните)

  2. DB First (сначала база). В этом варианте база уже создана и нам нужен обратный процесс - реконструировать модели и контекст по имеющейся БД. Мы будем использовать этот вариант.

В терминале (можно и в системной консоли, но обязательно в каталоге проекта)

выполните команду (естественно, вписав свои базы и пароли):

Перед этим, возможно, нужно выполнить команду dotnet tool install --global dotnet-ef (спасибо Андрею Кропинову).

dotnet ef dbcontext scaffold "server=kolei.ru;database=esmirnov;uid=esmirnov;password=111103;" MySql.EntityFrameworkCore -o esmirnov -f

параметры команды:

  • "строка подключения": здесь вариант для MySQL

    • server: доменное имя или IP-адрес севрера (на демо экзамене будет что-то типа 192.168.1.32)
    • database: название базы данных на сервере MySQL. Для лабораторных работ выдаёт преподаватель, на демо экзамене userXX
    • uid: логин пользователя (обычно то же, что и название БД, но если у вас свой сервер MySQL, то там вы рулите сами)
    • password: пароль пользователя
  • используемый провайдер БД (MySql.EntityFrameworkCore)

  • -o "Название каталога": название каталога в вашем проекте, в котором будут созданы контекст и модели БД

  • -f: перезаписывать модели (при повторном запуске команды)

Эта команда создает контекст подключения (параметры в кавычках) к провайдеру (второй параметр) MySQL и модели сущностей в папке esmirnov вашего проекта.

Например, рассмотрим таблицу Product:

public partial class Product
{
    public Product()
    {
        ProductCostHistories = new HashSet<ProductCostHistory>();
        ProductMaterials = new HashSet<ProductMaterial>();
        ProductSales = new HashSet<ProductSale>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public int ProductTypeId { get; set; }
    public string ArticleNumber { get; set; } = null!;
    public string? Description { get; set; }
    public string? Image { get; set; }
    public int? ProductionPersonCount { get; set; }
    public int? ProductionWorkshopNumber { get; set; }
    public decimal MinCostForAgent { get; set; }

    public virtual ProductType ProductType { get; set; } = null!;
    public virtual ICollection<ProductCostHistory> ProductCostHistories { get; set; }
    public virtual ICollection<ProductMaterial> ProductMaterials { get; set; }
    public virtual ICollection<ProductSale> ProductSales { get; set; }
}
  1. Для всех полей таблицы созданы свойства класса (Id, Title...), причём необязательные поля имеют "нуллабельные" типы (int? ProductionPersonCount).
  2. Для связей созданы виртуальные свойства или коллекции (в зависимости от направленности связи).

    • virtual ProductType ProductType - тип продукции (ссылка на словарь), отношение много (продуктов) к одному (типу продукта)
    • virtual ICollection ProductMaterials - материалы продукта - коллекция (список) материалов, используемых в этом продукте. Отношение один (продукт) ко многим (материалам продукта)
  3. Получение данных с сервера и вывод на экран.

    DataGrid в Avalonia не установлен по умолчанию. Установить можно двумя вариантами:

    • выполнив команду dotnet add package Avalonia.Controls.DataGrid. Тут может возникнуть ситуация, когда версия DataGrid больше чем версия Avalonia. В этом случае проект не соберется. Удалите пакет командой dotnet remove package Avalonia.Controls.DataGrid и воспользуйтесь вторым вариантом

    • Добавить ссылку в файл проекта:

      1. Переключите Explorer в режим File System:

      2. Откройте файл проекта *.csproj, найдите строчку <PackageReference Include="Avalonia" Version="11.0.2" /> (версия у вас может быть другая)

      3. Добавьте следующей строкой пакет <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.2" />

        При редактировании версии пакета можно нажать комбинацию клавишь Ctrl+Пробел и выйдет подсказка с возможными вариантами версий

    После добавления DataGrid нужно добавить стили в файл App.axaml (без них контрол не работает):

    <Application.Styles>
        <FluentTheme />
        <!-- стиль для DataGrid -->
        <StyleInclude 
            Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
    </Application.Styles>
    

    ВАЖНО!

    В последней версии Авалонии (11) привязка (binding) не работает, если не указать тип данных используемых в контроле (DataGrid, ListBox ...). Выдаёт ошибку "Unable to infer DataContext type for compiled binding"

    Типы данных у нас находятся в другом пространстве имён и для того, чтобы они стали доступны в текущем окне нужно добавить использование нужного пространства имён:

    <Window
       xmlns:model="using:AvaloniaApplication1.esmirnov"
    

    После чего, добавить в контрол аттрибут x:DataType:

    <DataGrid 
        x:DataType="model:Product"
    

    После этого у нас ломается текущий контекст и выдаёт ошибку, что не видит свойство productList: "Unable to resolve field or property 'productList' in data context of type 'AvaloniaApplication1.esmirnov.Product'"

    Чтобы это поправить, нужно указывать путь от корня окна. Для этого в окно добавляем аттрибут Name:

    <Window
       xmlns:model="using:AvaloniaApplication1.esmirnov"
       Name="root"
    

    И в DataGrid-e в привязке указываем путь от корня окна:

    x:DataType="model:Product"
    ItemsSource="{Binding #root.productList}"
    

    Для демонстрации работы в MainWindow.axaml добавим DataGrid:

    <DataGrid 
        Name="productsGrid"
        AutoGenerateColumns="False"
        x:DataType="model:Product"
        ItemsSource="{Binding #root.productList}"
    >
        <DataGrid.Columns>
            <DataGridTextColumn
                Header="Название"
                Binding="{Binding Title}"
            />
            <DataGridTextColumn
                Header="Номер"
                Binding="{Binding ArticleNumber}"
            />
            <DataGridTextColumn
                Header="Изображение"
                Binding="{Binding Image}"
            />
        </DataGrid.Columns>
    </DataGrid>
    

    и в коде проекта (MainWindow.axaml.cs) получим данные из БД:

    public partial class MainWindow : Window
    {
        public IEnumerable<Product> productList { get; set; }
        public MainWindow()
        {
            // считываем данные ДО инициализации окна, 
            // иначе без INotifyPropertyChanged
            // авалония не понимает, что данные изменились
            using (var context = new esmirnovContext())
            {
                productList = context.Products.ToList();
            }
            InitializeComponent();
        }
    }
    

    Обратите внимание, context.Products это не модель, а виртуальный DbSet (коллекция сущностей), объявленный в классе контекста: public virtual DbSet<Product> Products { get; set; }. При чтении этой коллекции как раз и происходит обращение к БД (посылка SQL-команд)

    Всё работает!!!

Создание базы данных. Импорт данных. Содержание Вывод данных согласно макету (ListView, Image).