Содержание
В предыдущей части мы рассмотрели работу с 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 и содержит модель, модель представления и представление.



