В следующем тесте демонстрируется пример создания исключения, связанного с параллелизмом, его перехвата и применения
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];
Одиночная сущность помечается для удаления путем вызова
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);