Entity Framework c Code-First миграциями для .Net Maui

Если вы, как и я, уже являетесь большим поклонником Microsoft Entity Framework и хотели бы начать использовать его локально в своем мобильном приложении, с появлением .Net Maui на рынке, это стало возможным.

Небольшое замечание: для ускорения времени запуска мобильного приложения может быть лучше хранить данные, используемые во время загрузки, в локальном хранилище мобильного устройства в форме json. Но, когда дело дойдет до работы с большими локальными данными, использования фильтров, сортировки и т. д., EF определенно подойдет идеально.

Цель этой статьи — помочь избежать хлопот, связанных с поиском различных решений небольших проблем при реализации production-ready мобильной локальной базы данных и создании для нее миграций на компьютерах Windows и Mac. Исходный код примера приложения доступен по ссылке, указанной в конце. Как вы сможете увидеть, это будет типовой шаблон приложения Maui с добавленной логикой базы данных EF.

Проверенным стандартом базы данных мобильного клиента является SQLite. Ну что ж, отправимся за пакетом nuget Microsoft.EntityFrameworkCore.Sqlite, также установим SQLitePCLRaw.bundle_e_sqlite3 для нативной поддержки sqlite. Для создания миграций EF нам также потребуется установить Microsoft.EntityFrameworkCore.Tools.

Для тех, кто не уверен, для чего нужны миграции, в основном, это код, который инструктирует EF о структуре базы данных для создания и использования. Важным преимуществом здесь является то, что когда вы меняете свои модели, вам лишь нужно будет сгенерировать утилитой дополнительные миграции для EF, чтобы автоматически изменить соответствующим образом существующую базу данных, добавляя-удаляя таблицы, столбцы и т. д.

Теперь, нюанс в том, что мы не можем установить эти нагеты в наш проект maui, потому что, как только мы попытаемся создать миграцию, то получим ошибку:

Startup project 'MauiEF.Client' targets platform 'Android'. The Entity Framework Core Package Manager Console Tools don't support this platform. See https://aka.ms/efcore-docs-pmc-tfms for more information.

… поэтому нужно будет найти способ это обойти. Поскольку мы уже знаем, что инструменты проектирования EF имеют неподдерживаемые платформы, нам нужно будет создать специальный проект «Мигратор», который будет напрямую запускаться EF и будет таргетить чистый .net 7.0, тогда сможем создавать миграции на своем компьютере, Windows или Mac.

И проект «Мигратор», и наш проект-клиент должны будут иметь доступ к базе данных, поэтому мы переместим весь наш код, связанный с ней, в отдельный общий проект. Тогда структура решения будет выглядеть так:

63665a2a3590e6b5096c7a37c641c0fb.jpg

Проект Shared будет иметь следующие зависимости

проект Migrator, для создания миграций, дополнительно потребует:

all runtime; build; native; contentfiles; analyzers; buildtransitive

Теперь перейдем к созданию модели БД. Определим ее в проекте Shared следующим образом:

d668bf1db0ba9e11517736e297e8edc0.JPG

Обратите внимание на два конструктора: один для средства миграции EF, второй — для нашего приложения. Database.Migrate(); создает базу данных, если она еще не существует, и/или применяет последние миграции.

Возможно, вы захотите также перед этим использовать метод Database.EnsureDeleted(); для того, чтобы стереть всю базу данных при запуске, иногда полезно в режиме отладки.

Если у вас появится критическое обновление приложения, вы также можете просто изменить имя файла базы данных, чтобы воссоздать базу данных с нуля для существующих пользователей.

Итак, теперь мы можем добавить зависимость для нашего контекста в MauiProgram.cs:

builder.Services.AddTransient((services) => { return new LocalDatabase(Path.Combine(FileSystem.AppDataDirectory, "SQLite001.db3")); });

Исходный код примера специально не включает миграции. Если вы просто скомпилируете и запустите решение, оно вызовет исключение, так как EF не будет знать, как вы хотите, чтобы она выполнила Migrate();.

Однако, миграцию очень легко создать.

Если вы используете Visual Studio для Windows:

Измените стартовый проект с Client на Migrator.

Откройте Консоль диспетчера пакетов: View->Other Windows->Package Manager Console. У меня нет VS на русском языке, подозреваю, что там это будет называться Вид->Другие окна->Консоль диспетчера пакетов.

Установите проект по умолчанию (default project) как Shared, EF будет искать модель контекста внутри и добавит в этот проект миграции.

Введите следующую команду, чтобы создать начальную миграцию:

add-migration Initial -Context MauiEF.Shared.Services.LocalDatabase -Verbose

c6d424434bb247b06e9658307c18607f.jpg

Следующий метод, использующий командной строку, используем в Visual Studio для Mac.

1 Откройте консоль: щелкните правой кнопкой мыши имя своего решения и выберите «Открыть в терминале». Вы должны попасть в папку решения.

2 Введите следующую команду, чтобы создать начальную миграцию через командную строку:

dotnet ef migrations add Initial -s Migrator -p Shared -c MauiEF.Shared.Services.LocalDatabase

Тут мы указали подпапку стартового проекта через -s Migrator и подпапку проекта по умолчанию через -p Shared.

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

dotnet tool install --global dotnet-ef export PATH="$PATH:/Users/YOUR_USERNAME/.dotnet/tools"

Каждый раз, когда вы поменяете модели контекста, вам придется создавать дополнительные миграции. Таким образом, ваше уже опубликованное приложение не сломается, если вы выпустите новую версию с измененными миграциями, EF (скорее всего) сохранит существующую базу данных пользователя и изменит ее в соответствии с новой схемой при инициализации вашей модели контекста. Почему «скорее всего»? Поскольку вы можете изменить свои модели таким образом, что связь или свойство внешнего ключа будут потеряны, но в этом случае EF предупредит вас при создании миграции с чем-то вроде «Warning data will be lost», так что управление данными в старых версиях приложения находится под вашим контролем.

Чтобы создать новую миграцию, просто создайте для нее уникальное имя (пример для Visual Studio для Windows):

add-migration Change1 -Context MauiEF.Shared.Services.LocalDatabase -Verbose

Теперь мы можем скомпилировать наше приложение и запустить его, созданные пользователем данные будут сохраняться локально на мобильном устройстве. Операции для работы с базой данных мы будем запускать асинхронно, не блокируя поток пользовательского интерфейса.

bc5c44e447eb141f932df9ace1ac4a70.JPG

Напоследок, небольшое примечание: когда вы скомпилируете ваше приложение EF Maui под iOS в Release-конфигурации, оно может вылететь на реальном устройстве, из-за того, что iOS AOT компиляция не поддерживает некоторые методы EF. Я бы не хотел быть здесь вдаваться в детали, вы можете прочитать об этом подробнее, но решение состоит в том, чтобы добавить немного специй в файл проекта клиента для этого конкретного случая:

--interpreter True

Приложения, скомпилированные с такими настройками, уже были опубликованы в AppStore, влияние на производительность не ощущается.

Надеюсь, эта небольшая статья окажется для вас полезной!

Исходный код примера с вариантом этой статьи на английском языке доступен на гитхаб.

© Habrahabr.ru