Содержание
В предыдущей части мы рассмотрели работу с SQLite в .NET MAUI с использованием библиотеки SQLite.NET. В этой части мы рассмотрим использование SQLite с технологией Microsoft Entity Framework Core (EF Core). Использование SQLite и EF Core в .NET MAUI имеет свои особенности и, чтобы их продемонстрировать, мы повторим приложение из предыдущей части, но уже с использованием EF Core.
Создание проекта и установка необходимых пакетов
Создадим новый проект .NET MAUI, добавим в проект папку Models, в которой разместим класс Project
:
public class Project { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Author { get; set; } }
Теперь установим в проект следующие NuGet-пакета:
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Sqlite
Теперь настроим наш проект .NET MAUI и EF Core для работы с моделью, которая представлена в проекте классом Project
.
Работа с SQLite и EF Core в .NET MAUI
Для того, чтобы наш проект мог взаимодействовать в БД SQLite с использованием EF Core мы должны создать контекст базы данных и зарегистрировать его в качестве сервиса в нашем приложении. Добавим в проект папку Services и разместим в ней класс ApplicationContext
:
using MauiEfCore.Models; using Microsoft.EntityFrameworkCore; namespace MauiEfCore.Services { public class ApplicationContext : DbContext { public DbSet<Project> Projects { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { Database.EnsureDeleted(); Database.EnsureCreated(); } } }
В конструкторе ApplicationContext мы удаляем и создаем заново базу данных SQLite. Для отладки приложения — это вполне рабочий вариант, но в рабочем проекте лучше использовать миграции о которых мы обязательно поговорим в контексте .NET MAUI.
Теперь зарегистрируем наш контекст базы данных SQLite в качестве сервиса. Для этого перейдем в файл MauiProgram.cs и изменим его следующим образом:
using Microsoft.Extensions.Logging; using MauiEfCore.Services; using MauiEfCore.ViewModels; using Microsoft.EntityFrameworkCore; namespace MauiEfCore; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); builder.Services.AddDbContext<ApplicationContext>(options => { options.UseSqlite("Data Source=databse.dat"); }); #if DEBUG builder.Logging.AddDebug(); #endif return builder.Build(); } }
Здесь мы регистрируем контекст базы данных как сервис, используя метод расширения IServiceCollection
AddDbContext
, указав в качестве параметра метода настройки подключения к базе:
builder.Services.AddDbContext<ApplicationContext>(options => { options.UseSqlite("Data Source=databse.dat"); });
На этом настройку EF Core в нашем приложении .NET MAUI можно считать законченной. Остается создать модель представления и подключить ей к странице приложения.
Организация доступа к данным SQLite с EF Core в .NET MAUI
Создадим в проекте новую папку ViewModels и разместим в ней класс MainPageViewModel
:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; using MauiEfCore.Services; using MauiEfCore.Models; using Microsoft.EntityFrameworkCore; namespace MauiEfCore.ViewModels { public class MainPageViewModel : INotifyPropertyChanged { private readonly ApplicationContext _database; public ObservableCollection<Project> Projects { get; set; } = []; private string _name = string.Empty; public string Name { get => _name; set { if (_name == value) return; _name = value; OnPropertyChanged(); } } private string _description = string.Empty; public string Description { get => _description; set { if (_description == value) return; _description = value; OnPropertyChanged(); } } private string _author = string.Empty; public string Author { get => _author; set { if (_author == value) return; _author = value; OnPropertyChanged(); } } private DateTime _deadline = DateTime.Now; public DateTime Deadline { get => _deadline; set { if (_deadline == value) return; _deadline = value; OnPropertyChanged(); } } public ICommand AddCommand { get; set; } public ICommand UpdateCommand { get; set; } public ICommand DeleteCommand { get; set; } public ICommand ReadCommand { get; set; } public ICommand SelectedItemCommand { get; set; } public MainPageViewModel(ApplicationContext database) { _database = database; AddCommand = new Command(async () => { Project project = new() { Name = this.Name, Description = this.Description, Author = this.Author }; await _database.Projects.AddAsync(project); int result = await _database.SaveChangesAsync(); if (result == 0) throw new Exception("Ошибка добавления новой записи в базу данных"); Projects.Add(project); Name = string.Empty; Description = string.Empty; Author = string.Empty; }); UpdateCommand = new Command<Project>(async (Project) => { Project.Name = this.Name; Project.Description = this.Description; Project.Author = this.Author; _database.Projects.Update(Project); int result = await _database.SaveChangesAsync(); if (result == 0) throw new Exception("Ошибка обновления записи в базе данных"); Projects.Remove(Project); ReadCommand?.Execute(null); }, (Param) => { return Param != null; }); DeleteCommand = new Command<Project>(async (Project) => { _database.Projects.Remove(Project); int result = await _database.SaveChangesAsync(); Projects.Remove(Project); if (result == 0) throw new Exception("Ошибка удаления записи из базы данных"); }, (Param) => { return Param != null; }); ReadCommand = new Command(async () => { var list = await _database.Projects.ToArrayAsync(); Projects.Clear(); foreach (var item in list) Projects.Add(item); OnPropertyChanged(nameof(Projects)); }); SelectedItemCommand = new Command<Project>((Project) => { Name = Project.Name; Description = Project.Description; Author = Project.Author; }, (Project) => { return Project != null; }); } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } } }
Наша модель представления содержит необходимые свойства создания нового или изменения существующего объекта Project
:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; using MauiEfCore.Services; using MauiEfCore.Models; using Microsoft.EntityFrameworkCore; namespace MauiEfCore.ViewModels { public class MainPageViewModel : INotifyPropertyChanged { public ObservableCollection<Project> Projects { get; set; } = []; private string _name = string.Empty; public string Name { get => _name; set { if (_name == value) return; _name = value; OnPropertyChanged(); } } private string _description = string.Empty; public string Description { get => _description; set { if (_description == value) return; _description = value; OnPropertyChanged(); } } private string _author = string.Empty; public string Author { get => _author; set { if (_author == value) return; _author = value; OnPropertyChanged(); } } private DateTime _deadline = DateTime.Now; public DateTime Deadline { get => _deadline; set { if (_deadline == value) return; _deadline = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } } }
Для уведомления об изменениях свойств модель представления реализует интерфейс INotifyPropertyChanged
. В конструкторе мы запрашиваем наш контекст базы данных:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; using MauiEfCore.Services; using MauiEfCore.Models; using Microsoft.EntityFrameworkCore; namespace MauiEfCore.ViewModels { public class MainPageViewModel : INotifyPropertyChanged { private readonly ApplicationContext _database; ... public MainPageViewModel(ApplicationContext database) { _database = database; ... } } }
для работы с базой данных мы также определили следующие команды:
SelectedItemCommand
— обновляет свойства, при выборе объекта в списке
SelectedItemCommand = new Command<Project>((Project) => { Name = Project.Name; Description = Project.Description; Author = Project.Author; }, (Project) => { return Project != null; });
ReadCommand
— обновляет список проектов в модели представления
ReadCommand = new Command(async () => { var list = await _database.Projects.ToArrayAsync(); Projects.Clear(); foreach (var item in list) Projects.Add(item); });
DeleteCommand
— удаляет проект из базы данных и списка Projects
DeleteCommand = new Command<Project>(async (Project) => { _database.Projects.Remove(Project); int result = await _database.SaveChangesAsync(); Projects.Remove(Project); if (result == 0) throw new Exception("Ошибка удаления записи из базы данных"); }, (Param) => { return Param != null; });
UpdateCommand
— обновляет проект, используя свойства модели представления
UpdateCommand = new Command<Project>(async (Project) => { Project.Name = this.Name; Project.Description = this.Description; Project.Author = this.Author; _database.Projects.Update(Project); int result = await _database.SaveChangesAsync(); if (result == 0) throw new Exception("Ошибка обновления записи в базе данных"); Projects.Remove(Project); ReadCommand?.Execute(null); }, (Param) => { return Param != null; });
AddCommand
— создает новый проект, используя свойства модели представления
AddCommand = new Command(async () => { Project project = new() { Name = this.Name, Description = this.Description, Author = this.Author }; await _database.Projects.AddAsync(project); int result = await _database.SaveChangesAsync(); if (result == 0) throw new Exception("Ошибка добавления новой записи в базу данных"); Projects.Add(project); Name = string.Empty; Description = string.Empty; Author = string.Empty; });
Обратите внимание на то, что при работе с SQLite и EF Core в .NET MAUI, везде где требуется изменение данных мы используем метод SaveChangesAsync()
. Например, при создании новой записи в БД:
await _database.Projects.AddAsync(project); //добавляем новый элемент int result = await _database.SaveChangesAsync();//сохраняем изменения
метод SaveChangesAsync()
сохраняет изменения в базе данных и возвращает количество строк, измененных в результате выполнения запроса.
Чтобы наша модель представления могла получать в качестве сервиса контекст базы данных, необходимо её также зарегистрировать как сервис. Добавим в MauiProgram.cs следующую строку:
builder.Services.AddSingleton<MainPageViewModel>();
Теперь нам необходимо использовать полученную модель представления на странице MainPage
.
Привязка свойств и команд модели представления при использовании SQLite и EF Core в .NET MAUI
Изменим код страницы MainPage
следующим образом:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiEfCore.MainPage" xmlns:local="clr-namespace:MauiEfCore.ViewModels" xmlns:models="clr-namespace:MauiEfCore.Models" x:DataType="local:MainPageViewModel"> <ScrollView> <VerticalStackLayout Padding="30,0" Spacing="25"> <VerticalStackLayout Padding="15" Margin="15"> <Label Text="Название проекта" /> <Entry Text="{Binding Name}" /> <Label Text="Описание" /> <Entry Text="{Binding Description}" /> <Label Text="Автор" /> <Entry Text="{Binding Author}" /> <CollectionView x:Name="ProjectsView" SelectionMode="Single" ItemsSource="{Binding Projects, Mode=TwoWay}" Margin="15" SelectionChangedCommand="{Binding SelectedItemCommand}" SelectionChangedCommandParameter="{Binding Source={RelativeSource Mode=Self},Path=SelectedItem}"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="{x:Type models:Project}"> <Border StrokeShape="Rectangle" Background="LightGreen"> <VerticalStackLayout> <Label Text="{Binding Name}" FontSize="16" FontAttributes="Bold" TextColor="Blue"/> <Label Text="{Binding Id}"/> <Label Text="{Binding Description}"/> <Label Text="{Binding Author}"/> </VerticalStackLayout> </Border> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </VerticalStackLayout> <HorizontalStackLayout HorizontalOptions="Center"> <Button Margin="10" Text="Обновить список" Command="{Binding ReadCommand}" /> <Button Margin="10" Text="Добавить" Command="{Binding AddCommand}" CommandParameter="{Binding Source={x:Reference ProjectsView}, Path=SelectedItem}"/> <Button Margin="10" Text="Удалить" Command="{Binding DeleteCommand}" CommandParameter="{Binding Source={x:Reference ProjectsView}, Path=SelectedItem}"/> <Button Margin="10" Text="Изменить" Command="{Binding UpdateCommand}" CommandParameter="{Binding Source={x:Reference ProjectsView}, Path=SelectedItem}"/> </HorizontalStackLayout> </VerticalStackLayout> </ScrollView> </ContentPage>
Здесь мы установили необходимые привязки к командам и свойствам модели представления, как мы это делали ранее. Остается только передать нашу модель представления на страницу. Для этого изменим код MainPage.xaml.cs следующим образом:
using MauiEfCore.ViewModels; namespace MauiEfCore { public partial class MainPage : ContentPage { public MainPage(MainPageViewModel model) { InitializeComponent(); BindingContext = model; } } }
Проверим работу приложения
Итого
Для использования SQLite и EF Core в .NET MAUI нам необходимо установить в проект необходимые NuGet-пакеты, создать контекст базы данных и подключить его в проект в качестве сервиса. качестве примера мы разработали небольшое приложение, использующее базу данных SQLite для хранения данных о проектах. Наше приложение использует шаблон MVVM и содержит модель, модель представления и представление.