Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

В следующем тесте демонстрируется пример создания исключения, связанного с параллелизмом, его перехвата и применения

Entries
для получения исходных значений, текущих значений и значений, которые в настоящий момент хранятся в базе данных. Получение текущих значений требует еще одного обращения к базе данных:

[Fact]

public void ShouldThrowConcurrencyException

{

ExecuteInATransaction(RunTheTest);

void RunTheTest

{

var car = Context.Cars.First;

//
Обновить базу данных за пределами контекста.

Context.Database.ExecuteSqlInterpolated(

$"Update dbo.Inventory set Color='Pink' where Id = {car.Id}");

car.Color = "Yellow";

var ex = Assert.Throws<CustomConcurrencyException>(

=> Context.SaveChanges);

var entry = ((DbUpdateConcurrencyException) ex.InnerException)?.Entries[0];

PropertyValues originalProps = entry.OriginalValues;

PropertyValues currentProps = entry.CurrentValues;

// Требует еще одного обращения к базе данных.

PropertyValues databaseProps = entry.GetDatabaseValues;

}

}

Ниже показаны выполняемые операторы SQL. Первым из них является оператор UPDATE, а вторым — обращение для получения значений базы данных:

exec sp_executesql N'SET NOCOUNT ON;

UPDATE [dbo].[Inventory] SET [Color] = @p0

WHERE [Id] = @p1 AND [TimeStamp] = @p2;

SELECT [TimeStamp]

FROM [dbo].[Inventory]

WHERE @@ROWCOUNT = 1 AND [Id] = @p1;'

,N'@p1 int,@p0 nvarchar(50),@p2 varbinary(8)',@p1=1,@p0=N'Yellow',

@p2=0x0000000000008665

exec sp_executesql N'SELECT TOP(1) [i].[Id], [i].[Color],

[i].[IsDrivable], [i].[MakeId],
[i].[PetName], [i].[TimeStamp]

FROM [dbo].[Inventory] AS [i]

WHERE [i].[Id] = @__p_0',N'@__p_0 int',@__p_0=1

Удаление записей

Одиночная сущность помечается для удаления путем вызова

Remove
на
DbSet<T>
или установки ее состояния в
Deleted
. Список записей помечается для удаления вызовом
RemoveRange
на
DbSet<T>
. Процесс удаления будет вызывать эффекты каскадирования для навигационных свойств на основе правил, сконфигурированных в методе
OnModelCreating
(и регламентированных соглашениями EF Core). Если удаление не допускается из -за политики каскадирования, тогда генерируется исключение.

Состояние сущности

Когда метод

Remove
вызывается на отслеживаемой сущности, свойство
EntityState
устанавливается в
Deleted
. После успешного выполнения оператора удаления сущность исключается из
ChangeTracker
и состояние изменяется на
Detached
. Обратите внимание, что сущность по-прежнему существует в вашем приложении, если
только она не покинула область видимости и не была подвержена сборке мусора.

Удаление отслеживаемых сущностей

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

Remove
на контексте и затем вызовите
SaveChanges
, чтобы удалить запись из базы данных:

[Fact]

public void ShouldRemoveACar

{

ExecuteInATransaction(RunTheTest);

void RunTheTest

{

var carCount = Context.Cars. Count;

var car = Context.Cars.First(c => c.Id == 2);

Context.Cars.Remove(car);

Context.SaveChanges;

var newCarCount = Context.Cars.Count;

Assert.Equal(carCount - 1, newCarCount);

Assert.Equal(

EntityState.Detached,

Context.Entry(car).State);

}

}

После вызова

SaveChanges
экземпляр сущности все еще существует, но больше не находится в
ChangeTracker
. Состоянием
EntityState
будет
Detached
. Вот как выглядит выполняемый код SQL:

exec sp_executesql N'SET NOCOUNT ON;

DELETE FROM [dbo].[Inventory]

WHERE [Id] = @p0 AND [TimeStamp] = @p1;

SELECT @@ROWCOUNT;'

,N'@p0 int,@p1 varbinary(8)',@p0=2,

@p1=0x0000000000008680

Удаление неотслеживаемых сущностей

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

Remove/RemoveRange
или установкой состояния в
Deleted
и последующим вызовом
SaveChanges
.

В показанном ниже тесте сначала читается запись как неотслеживаемая и на основе записи создается новый экземпляр класса

Car
. Затем либо устанавливается состояние в
Deleted
, либо применяется метод
Remove
класса
DbSet<T>
(в зависимости от того, какая строка кода закомментирована) и вызывается
SaveChanges
. Все дополнительные контексты нужны для обеспечения точности теста и отсутствия пересечения между контекстами:

[Fact]

public void ShouldRemoveACarUsingState

{

ExecuteInASharedTransaction(RunTheTest);

void RunTheTest(IDbContextTransaction trans)

{

var carCount = Context.Cars.Count;

var car = Context.Cars.AsNoTracking.First(c => c.Id == 2);

var context2 = TestHelpers.GetSecondContext(Context, trans);

Поделиться с друзьями: