From 7be8f115bee35715b56dd87f3a79e832fdb6ed62 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Mon, 19 Sep 2022 10:38:21 +0200 Subject: [PATCH] Sproc test fixes around concurrency tokens Closes #29135 --- .../Update/StoredProcedureUpdateTestBase.cs | 50 +++++++++++++------ .../StoredProcedureUpdateSqlServerTest.cs | 20 ++++---- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/test/EFCore.Relational.Specification.Tests/Update/StoredProcedureUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/StoredProcedureUpdateTestBase.cs index 6a7abce9896..ddac87ace54 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/StoredProcedureUpdateTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/StoredProcedureUpdateTestBase.cs @@ -404,17 +404,26 @@ public virtual async Task Store_generated_concurrency_token_as_in_out_parameter( await using (var context2 = CreateContext()) { var entity2 = await context2.WithStoreGeneratedConcurrencyTokenAsInOutParameter.SingleAsync(w => w.Name == "Initial"); - entity2.Name = "Preempted"; - await SaveChanges(context2, async); - } + entity2.Name = "Updated"; - ClearLog(); + entity1.Name = "Preempted"; + await SaveChanges(context1, async); - entity1.Name = "Updated"; + ClearLog(); - var exception = await Assert.ThrowsAsync(async () => await SaveChanges(context1, async)); - var entry = exception.Entries.Single(); - Assert.Same(entity1, entry.Entity); + var exception = await Assert.ThrowsAsync(async () => await SaveChanges(context2, async)); + var entry = exception.Entries.Single(); + Assert.Same(entity2, entry.Entity); + } + + // Make sure we propagated the new concurrency token in the update above + using (Fixture.TestSqlLoggerFactory.SuspendRecordingEvents()) + { + entity1.Name = "Another update"; + await SaveChanges(context1, async); + + Assert.Equal("Another update", (await context1.WithStoreGeneratedConcurrencyTokenAsInOutParameter.SingleAsync(w => w.Id == entity1.Id)).Name); + } } [ConditionalTheory] @@ -430,17 +439,26 @@ public virtual async Task Store_generated_concurrency_token_as_two_parameters(bo await using (var context2 = CreateContext()) { var entity2 = await context2.WithStoreGeneratedConcurrencyTokenAsTwoParameters.SingleAsync(w => w.Name == "Initial"); - entity2.Name = "Preempted"; - await SaveChanges(context2, async); - } + entity2.Name = "Updated"; - ClearLog(); + entity1.Name = "Preempted"; + await SaveChanges(context1, async); - entity1.Name = "Updated"; + ClearLog(); - var exception = await Assert.ThrowsAsync(async () => await SaveChanges(context1, async)); - var entry = exception.Entries.Single(); - Assert.Same(entity1, entry.Entity); + var exception = await Assert.ThrowsAsync(async () => await SaveChanges(context2, async)); + var entry = exception.Entries.Single(); + Assert.Same(entity2, entry.Entity); + } + + // Make sure we propagated the new concurrency token in the update above + using (Fixture.TestSqlLoggerFactory.SuspendRecordingEvents()) + { + entity1.Name = "Another update"; + await SaveChanges(context1, async); + + Assert.Equal("Another update", (await context1.WithStoreGeneratedConcurrencyTokenAsTwoParameters.SingleAsync(w => w.Id == entity1.Id)).Name); + } } [ConditionalTheory] diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/StoredProcedureUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/StoredProcedureUpdateSqlServerTest.cs index 4a67e71ef99..4f7e65240d3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Update/StoredProcedureUpdateSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Update/StoredProcedureUpdateSqlServerTest.cs @@ -260,13 +260,11 @@ public override async Task Store_generated_concurrency_token_as_two_parameters(b // Can't assert SQL baseline as usual because the concurrency token changes Assert.Equal( - @"@p2='Updated' (Size = 4000) -@p3=NULL (Size = 8) (Direction = Output) (DbType = Binary) -@p4='0' (Direction = Output) + @"@p4='0' (Direction = Output) SET NOCOUNT ON; EXEC [WithStoreGeneratedConcurrencyTokenAsTwoParameters_Update] @p0, @p1, @p2, @p3 OUTPUT, @p4 OUTPUT;", - Fixture.TestSqlLoggerFactory.Sql.Substring(Fixture.TestSqlLoggerFactory.Sql.IndexOf("@p2", StringComparison.Ordinal)), + Fixture.TestSqlLoggerFactory.Sql.Substring(Fixture.TestSqlLoggerFactory.Sql.IndexOf("@p4", StringComparison.Ordinal)), ignoreLineEndingDifferences: true); } @@ -526,16 +524,20 @@ AS BEGIN CREATE PROCEDURE WithStoreGeneratedConcurrencyTokenAsInOutParameter_Update(@Id int, @ConcurrencyToken rowversion OUT, @Name varchar(max), @RowsAffected int OUT) AS BEGIN - UPDATE [WithStoreGeneratedConcurrencyTokenAsInOutParameter] SET [Name] = @Name WHERE [Id] = @Id AND [ConcurrencyToken] = @ConcurrencyToken; + DECLARE @TempTable table ([ConcurrencyToken] varbinary(8)); + UPDATE [WithStoreGeneratedConcurrencyTokenAsInOutParameter] SET [Name] = @Name OUTPUT INSERTED.[ConcurrencyToken] INTO @TempTable WHERE [Id] = @Id AND [ConcurrencyToken] = @ConcurrencyToken; SET @RowsAffected = @@ROWCOUNT; + SELECT @ConcurrencyToken = [ConcurrencyToken] FROM @TempTable; END; GO CREATE PROCEDURE WithStoreGeneratedConcurrencyTokenAsTwoParameters_Update(@Id int, @ConcurrencyTokenIn rowversion, @Name varchar(max), @ConcurrencyTokenOut rowversion OUT, @RowsAffected int OUT) AS BEGIN - UPDATE [WithStoreGeneratedConcurrencyTokenAsTwoParameters] SET [Name] = @Name, @ConcurrencyTokenOut = [ConcurrencyToken] WHERE [Id] = @Id AND [ConcurrencyToken] = @ConcurrencyTokenIn; + DECLARE @TempTable table ([ConcurrencyToken] varbinary(8)); + UPDATE [WithStoreGeneratedConcurrencyTokenAsTwoParameters] SET [Name] = @Name OUTPUT INSERTED.[ConcurrencyToken] INTO @TempTable WHERE [Id] = @Id AND [ConcurrencyToken] = @ConcurrencyTokenIn; SET @RowsAffected = @@ROWCOUNT; + SELECT @ConcurrencyTokenOut = [ConcurrencyToken] FROM @TempTable; END; GO @@ -578,10 +580,10 @@ IF @Name IS NULL CREATE PROCEDURE Tph_Insert(@Id int OUT, @Discriminator varchar(max), @Name varchar(max), @Child2InputProperty int, @Child2OutputParameterProperty int OUT, @Child1Property int) AS BEGIN - DECLARE @Table table ([Child2OutputParameterProperty] int); - INSERT INTO [Tph] ([Discriminator], [Name], [Child1Property], [Child2InputProperty]) OUTPUT [Inserted].[Child2OutputParameterProperty] INTO @Table VALUES (@Discriminator, @Name, @Child1Property, @Child2InputProperty); + DECLARE @TempTable table ([Child2OutputParameterProperty] int); + INSERT INTO [Tph] ([Discriminator], [Name], [Child1Property], [Child2InputProperty]) OUTPUT [Inserted].[Child2OutputParameterProperty] INTO @TempTable VALUES (@Discriminator, @Name, @Child1Property, @Child2InputProperty); SET @Id = SCOPE_IDENTITY(); - SELECT @Child2OutputParameterProperty = [Child2OutputParameterProperty] FROM @Table; + SELECT @Child2OutputParameterProperty = [Child2OutputParameterProperty] FROM @TempTable; SELECT [Child2ResultColumnProperty] FROM [Tph] WHERE [Id] = @Id END;