Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
...
ChangeTracker.Tracked += ChangeTracker_Tracked;
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
}
Аргументы события Tracked содержат ссылку на сущность, которая инициировала событие, и указывают, было оно получено из запроса (загруженного из базы данных) или добавлено программно. Добавьте в класс ApplicationDbContext следующий обработчик событий:
private void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e)
{
var source = (e.FromQuery) ? "Database" : "Code";
if (e.Entry.Entity is Car c)
{
Console.WriteLine($"Car entry {c.PetName} was added from {source}");
}
}
Событие StateChanged инициируется при изменении состояния сущности. Одно из применений этого события — аудит. Поместите в класс ApplicationDbContext приведенный ниже обработчик событий. Если свойство NewState сущности имеет значение Unchanged, тогда выполняется проверка свойства OldState для выяснения, сущность была добавлена или же модифицирована.
private void ChangeTracker_StateChanged(object? sender,
EntityStateChangedEventArgs e)
{
if (e.Entry.Entity is not Car c)
{
return;
}
var action = string.Empty;
Console.WriteLine($"Car {c.PetName}
was {e.OldState} before the state changed to {e.NewState}");
switch (e.NewState)
{
case EntityState.Unchanged:
action = e.OldState switch
{
EntityState.Added => "Added",
EntityState.Modified => "Edited",
_ => action
};
Console.WriteLine($"The object was {action}");
break;
}
}
Создание миграции и обновление базы данных
На этой стадии оба проекта компилируются и все готово к созданию еще одной миграции для обновления базы данных. Введите в каталоге проекта AutoLot.Dal следующие команды (каждая команда должна вводиться в одной строке):
dotnet ef migrations add UpdatedEntities -o EfStructuresMigrations
-c AutoLot.Dal.EfStructures.ApplicationDbContext
dotnet ef database update UpdatedEntities
-c AutoLot.Dal.EfStructures.ApplicationDbContext
Добавление представления базы данных и хранимой процедуры
Осталось внести в базу данных два изменения: создать хранимую процедуру GetPetName, рассмотренную в главе 21, и добавить представление базы данных, которое объединяет таблицу Orders с деталями Customer, Car и Make.
Добавление класса MigrationHelpers
Хранимая процедура и представление будут создаваться с использованием миграции, которая требует написания кода вручную. Причина поступать так (вместо того, чтобы просто открыть Azure Data Studio и запустить код T-SQL) — желание поместить полное конфигурирование базы данных в один процесс. Когда все содержится в миграциях, единственный вызов dotnet ef database update гарантирует, что база данных является актуальной, включая конфигурацию EF Core и специальный код SQL.
Выполнение команды the dotnet ef migrations add при отсутствии изменений в модели все равно приводит к созданию файлов миграции, имеющих правильную отметку времени, с пустыми методами Up() и Down(). Введите показанную ниже команду для создания пустой миграции (но не применения миграции):
dotnet ef migrations add SQL -o EfStructuresMigrations
-c AutoLot.Dal.EfStructures.ApplicationDbContext
Создайте в каталоге EfStructures проекта AutoLot.Dal новый файл по имени MigrationHelpers.cs. Добавьте оператор using для пространства имен Microsoft.EntityFrameworkCore.Migrations, сделайте класс открытым и статическим и поместите в него следующие методы, которые используют MigrationBuilder для запуска операторов SQL в отношении базы данных:
namespace AutoLot.Dal.EfStructures
{
public static class MigrationHelpers
{
public static void CreateSproc(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql($@"
exec (N'
CREATE PROCEDURE [dbo].[GetPetName]
@carID int,
@petName nvarchar(50) output
AS
SELECT @petName = PetName from dbo.Inventory where Id = @carID
')");
}
public static void DropSproc(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DROP PROCEDURE [dbo].[GetPetName]");
}
public static void CreateCustomerOrderView(
MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql($@"
exec (N'
CREATE VIEW [dbo].[CustomerOrderView]
AS
SELECT dbo.Customers.FirstName, dbo.Customers.LastName,
dbo.Inventory.Color, dbo.Inventory.PetName,
dbo.Inventory.IsDrivable,
dbo.Makes.Name AS Make
FROM dbo.Orders
INNER JOIN dbo.Customers ON dbo.Orders.CustomerId = dbo.Customers.Id
INNER JOIN dbo.Inventory ON dbo.Orders.CarId = dbo.Inventory.Id
INNER JOIN dbo.Makes ON dbo.Makes.Id = dbo.Inventory.MakeId
')");
}
public static void DropCustomerOrderView(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("EXEC (N' DROP VIEW [dbo].[CustomerOrderView] ')");
}
}
}
Обновление и применение миграции
Для каждого объекта SQL Server в классе MigrationHelpers имеется два метода: один создает объект, другой удаляет объект. Вспомните, что при применении миграции выполняется метод Up(), а при откате миграции — метод Down(). Вызовы статических методов создания должны попасть в метод Up() миграции, тогда как вызовы статических методов удаления — в метод Down() миграции. В результате применения миграции создаются два объекта SQL Server, которые в случае отката миграции благополучно удаляются. Ниже приведен модифицированный код миграции:
namespace AutoLot.Dal.EfStructures.Migrations
{
public partial class SQL : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
MigrationHelpers.CreateSproc(migrationBuilder);
MigrationHelpers.CreateCustomerOrderView(migrationBuilder);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
MigrationHelpers.DropSproc(migrationBuilder);
MigrationHelpers.DropCustomerOrderView(migrationBuilder);
}
}
}
Если вы удалили свою базу данных, чтобы запустить начальную миграцию, тогда можете применить эту миграцию и двигаться дальше. Примените миграцию, выполнив следующую команду:
dotnet ef database update -c AutoLot.Dal.EfStructures.ApplicationDbContext
если вы не удаляли свою базу данных для первой миграции, то процедура уже существует и не может быть снова создана. В таком случае легче всего закомментировать в методе Up() вызов статического метода, создающего хранимую процедуру:
protected override void Up(MigrationBuilder migrationBuilder)
{
// MigrationHelpers.CreateSproc(migrationBuilder);
MigrationHelpers.CreateCustomerOrderView(migrationBuilder);
}
После применения полученной миграции в первый раз уберите комментарий с указанной выше строки и все будет работать нормально. Разумеется, можно