Язык программирования C#9 и платформа .NET5
context2.Entry(updatedCar).State = EntityState.Modified;В любом случае для сохранения значений все равно должен вызываться метод
.SaveChanges()В представленном далее тесте читается неотслеживаемая запись, из нее создается новый экземпляр класса
и изменяется одно его свойство (Car). Затем в зависимости от того, с какой строки кода вы уберете комментарий, либо устанавливается состояние, либо использует методColorнаUpdate(). МетодDbSet<T>также изменяет состояние наUpdate(). Затем в тесте вызывается методModified. Все дополнительные контексты нужны для обеспечения точности теста и отсутствия пересечения между контекстами:SaveChanges()[Fact]public void ShouldUpdateACarUsingState(){ExecuteInASharedTransaction(RunTheTest);void RunTheTest(IDbContextTransaction trans){var car = Context.Cars.AsNoTracking().First(c => c.Id == 1);Assert.Equal("Black", car.Color);var updatedCar = new Car{Color = "White", //Original is BlackId = car.Id,MakeId = car.MakeId,PetName = car.PetName,TimeStamp = car.TimeStampIsDrivable = car.IsDrivable};var context2 = TestHelpers.GetSecondContext(Context, trans);// Либо вызвать Update(), либо модифицировать состояние.context2.Entry(updatedCar).State = EntityState.Modified;// context2.Cars.Update(updatedCar);context2.SaveChanges();var context3 =TestHelpers.GetSecondContext(Context, trans);var car2 = context3.Cars.First(c => c.Id == 1);Assert.Equal("White", car2.Color);}}Ниже показан выполняющийся оператор SQL:
exec sp_executesql N'SET NOCOUNT ON;UPDATE [dbo].[Inventory] SET [Color] = @p0WHERE [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'White',@p2=0x000000000000862DПроверка параллелизма
Проверка параллелизма подробно обсуждалась в предыдущей главе. Вспомните, что когда внутри сущности определено свойство
, то значение этого свойства используется в конструкцииTimeStampпри сохранении изменений (обновлений или удалений) в базе данных. Вместо поиска только первичного ключа к запросу добавляется поиск значенияWHERE, например:TimeStampUPDATE [dbo].[Inventory] SET [PetName] = @p0WHERE [Id] = @p1 AND <b>[TimeStamp] = @p2</b>;В следующем тесте демонстрируется пример создания исключения, связанного с параллелизмом, его перехвата и применения
для получения исходных значений, текущих значений и значений, которые в настоящий момент хранятся в базе данных. Получение текущих значений требует еще одного обращения к базе данных: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] = @p0WHERE [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',