Эта статья является частью серии о .NET Core и о том, как теперь он стал таким же простым и эффективным, как и любой другой стек, такой как Node или Rails, для создания современных приложений.
.NET Core - это привлекательно - приложение командной строки
.NET Core - это круто - создание веб-API
Я намерен создать приложение для курирования, которое мы будем улучшать шаг за шагом, и в итоге мы получим платформу, полностью готовую к производству. Для начала мы начнем создавать простое приложение командной строки с базой данных Sqlite.
Наше целевое приложение
На этом этапе мы создаем приложение командной строки, которое будем использовать для хранения элементов, определяемых уникальным идентификатором, именем и датой создания. После этого мы сможем отобразить наш текущий список курирования и удалить из него элемент.
curator add medium.com/@jbuisson curator add blog.cleancoder.com curator list > 1: medium.com/@jbuisson, 8/3/19 9:58:56 AM. > 2: blog.cleancoder.com, 8/3/19 9:59:17 AM. curator remove 1 curator list > 2: blog.cleancoder.com, 8/3/19 9:59:17 AM.
Настроить приложение и пакеты
Создайте новое консольное приложение:
dotnet new console -o Curator
Мы будем использовать Microsoft.EntityFramework в качестве объектно-реляционного сопоставления (ORM) нашей базы данных, настроенного для Sqlite. Кроме того, мы будем использовать CommandLineUtils от Нейта Макмастера - отличный пакет для простого написания приложений командной строки.
Изнутри нашего недавно созданного проекта выполните следующие команды, чтобы добавить пакеты:
dotnet add package McMaster.Extensions.CommandLineUtils dotnet add package Microsoft.EntityFrameworkCore.Sqlite dotnet add package Microsoft.EntityFrameworkCore.Design
Время писать код
Теперь мы должны создать нашу модель Item, чтобы сохранить ее в базе данных Sqlite.
У нас есть класс C # с общедоступными свойствами для получения / установки Id, Name и CreatedAt. Мы также переопределяем метод ToString () для отображения информации об элементе.
Затем мы создаем EntityFramework DbContext и настраиваем его для использования Sqlite.
Опять же, здесь нет ничего хитрого; DbContext служит мостом для доступа к данным из базы данных. Мы объявляем DbSet для модели Item, что указывает на то, что у нас есть Items в базе данных.
Кроме того, мы сообщаем EntityFramework UseSqlite путем указания пути к файлу базы данных Sqlite. Этот путь относительно двоичного каталога выполнения. Не стесняйтесь использовать любой другой путь, который считаете подходящим, но убедитесь, что целевой каталог существует; в противном случае вы можете столкнуться со следующей ошибкой приложения:
SQLite Error 14: ‘unable to open database file’.
Перед настройкой самого приложения мы создаем новую миграцию EntityFramework и запускаем ее для настройки базы данных.
dotnet ef migrations add InitialCreate dotnet ef database update
Вы должны увидеть файл curator.db в каталоге, который вы ранее настроили в DbContext. Если вы не меняли путь источника данных, вы сможете найти его в корне рабочей области.
Наконец, мы создаем наши команды.
Код явный. У нас есть базовый класс CuratorCommand, который работает как родительский класс для всех других команд и отвечает за CuratorContext . Кроме того, у нас есть еще три класса для следующих трех команд: добавить, список, удалить.
Я призываю вас изучить команды. Подумайте даже о создании своего собственного, чтобы ознакомиться с приложением и его работой.
Последний шаг - обновить основную точку входа в наше приложение. Это должно привести к примерно следующему:
Пришло время проверить это.
dotnet run add medium.com/@jbuisson dotnet run add blog.cleancoder.com dotnet run list > 1: medium.com/@jbuisson, 8/3/19 11:13:37 AM. > 2: blog.cleancoder.com, 8/3/19 11:13:42 AM. dotnet run remove 1 dotnet run list > 2: blog.cleancoder.com, 8/3/19 11:13:42 AM.
Время рефакторинга
У нас есть работающее приложение из командной строки, но оно плохо спроектировано и не соблюдает принцип чистого кода.
Я стремлюсь к этой архитектуре проекта, разделив консольное приложение на несколько библиотек классов:
├── data ├── src ├── Curator.Console ├── Curator.Data ├── Curator.Data.Entities ├── Curator.Data.EntityFramework ├── Curator.Data.EntityFramework.Context ├── Curator.Data.EntityFramework.Sqlite ├── tests
Это может показаться несколько сложным - то есть иметь такую глубину и разбивать Entities, Context и Sqlite на три разные библиотеки. Однако использование этого метода поддерживается по нескольким причинам:
- Я должен иметь возможность использовать Entities без EntityFramework; это две проблемы со своими собственными обязанностями, и так далее следует разделять. Это не догматическое видение, а прагматическое. В какой-то момент мы могли бы удалить EntityFramework, чтобы использовать другой ORM - или вообще не использовать - и это не будет слишком дорогостоящим благодаря этому подходу.
- EntityFramework позволяет нам использовать широкий спектр баз данных. Это означает, что при использовании одного и того же контекста мы могли использовать разные базы данных. Спойлер: мы сделаем это как при тестировании, так и при портировании приложения в веб-стек.
Папка данных просто служит нашим хранилищем Sqlite. Папка tests говорит сама за себя и будет использоваться после завершения рефакторинга.
Я также включу несколько проверок времени выполнения на предмет наличия null или недопустимых аргументов.
Писать тесты никогда не поздно
Я не могу осознанно рассматривать этот шаг без добавления некоторых тестов. Еще раз вы увидите, что легко настроить тесты на основе вашего кода, а затем запускать их.
Чтобы проверить нашу команду, мы не хотим использовать настоящую базу данных Sqlite. EntityFramework имеет поставщика InMemory, чтобы помочь нам в этом.
cd src/Curator.Data/Curator.Data.EntityFramework dotnet new classlib -o Curator.Data.EntityFramework.Memory cd Curator.Data.EntityFramework.Memory dotnet add package Microsoft.EntityFrameworkCore.InMemory dotnet add reference ../Curator.Data.EntityFramework/Curator.Data.EntityFramework.Context/
Теперь нам нужно только создать новую DesignTimeDbContextFactory и настроить ее с помощью UseInMemoryDatabase. Уловка здесь состоит в том, чтобы добавить уникальное имя базы данных в каждый новый контекст DbContext, чтобы изолировать каждый тест. Для этого я использую DateTime.Now.Ticks, который достаточно безопасен, но вместо этого вы можете использовать Guid.NewGuid () или любой уникальный генератор случайных чисел.
Как только это будет завершено, мы должны перейти в папку тестов и создать новый тестовый проект.
cd tests dotnet new xunit -o Curator.Console.Tests
Мы должны добавить ссылки на проект, который мы хотим протестировать, в дополнение к новому провайдеру базы данных, который мы только что создали:
dotnet add reference ../../src/Curator.Console dotnet add reference ..\..\src\Curator.Data\Curator.Data.EntityFramework\Curator.Data.EntityFramework.Memory\Curator.Data.EntityFramework.Memory.csproj
Теперь вы можете написать простой тест для AddCommand:
Наконец, когда все тесты написаны, у нас есть чистое приложение командной строки, использующее .NET Core, совместимое с Linux, Windows и Mac! Из моей предыдущей истории вы теперь сможете легко выпустить это приложение:
dotnet publish src/Curator.Console -c Release — self-contained true -r [YOUR_RUNTIME_IDENTIFIER] -o [INSTALLATION_PATH]
Это наш первый шаг. Следующим будет веб-API, развернутый на сервере или с помощью Docker.