Skip to content

Commit

Permalink
Sync EF Core to 7.0.0-preview.7.22359.1 (#2422)
Browse files Browse the repository at this point in the history
  • Loading branch information
roji authored Jul 9, 2022
1 parent ba7d2a5 commit 037cffd
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 65 deletions.
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<EFCoreVersion>7.0.0-preview.6.22322.4</EFCoreVersion>
<MicrosoftExtensionsVersion>7.0.0-preview.6.22319.5</MicrosoftExtensionsVersion>
<EFCoreVersion>7.0.0-preview.7.22359.1</EFCoreVersion>
<MicrosoftExtensionsVersion>7.0.0-preview.6.22354.1</MicrosoftExtensionsVersion>
<NpgsqlVersion>7.0.0-preview.5</NpgsqlVersion>
</PropertyGroup>

Expand Down
133 changes: 106 additions & 27 deletions src/EFCore.PG/Update/Internal/NpgsqlModificationCommandBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ protected override void Consume(RelationalDataReader reader)
#pragma warning disable 618
if (npgsqlReader.Statements[commandIndex].Rows == 0)
{
throw new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(1, 0),
ModificationCommands[commandIndex].Entries
);
ThrowAggregateUpdateConcurrencyException(reader, commandIndex, 1, 0);
}
#pragma warning restore 618
}
Expand All @@ -77,27 +74,23 @@ protected override void Consume(RelationalDataReader reader)

// Propagate to results from the reader to the ModificationCommand

var modificationCommand = ModificationCommands[commandIndex++];
var modificationCommand = ModificationCommands[commandIndex];

if (!reader.Read())
{
throw new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(1, 0),
modificationCommand.Entries);
ThrowAggregateUpdateConcurrencyException(reader, commandIndex, 1, 0);
}

Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false");

modificationCommand.PropagateResults(reader);

npgsqlReader.NextResult();

commandIndex++;
}
}
catch (DbUpdateException)
{
throw;
}
catch (Exception ex)
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
{
throw new DbUpdateException(
RelationalStrings.UpdateStoreException,
Expand Down Expand Up @@ -138,10 +131,8 @@ protected override async Task ConsumeAsync(
#pragma warning disable 618
if (npgsqlReader.Statements[commandIndex].Rows == 0)
{
throw new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(1, 0),
ModificationCommands[commandIndex].Entries
);
await ThrowAggregateUpdateConcurrencyExceptionAsync(reader, commandIndex, 1, 0, cancellationToken)
.ConfigureAwait(false);
}
#pragma warning restore 618
}
Expand All @@ -154,33 +145,121 @@ protected override async Task ConsumeAsync(

// Extract result from the command and propagate it

var modificationCommand = ModificationCommands[commandIndex++];
var modificationCommand = ModificationCommands[commandIndex];

if (!(await reader.ReadAsync(cancellationToken).ConfigureAwait(false)))
{
throw new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(1, 0),
modificationCommand.Entries
);
await ThrowAggregateUpdateConcurrencyExceptionAsync(reader, commandIndex, 1, 0, cancellationToken)
.ConfigureAwait(false);
}

Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false");

modificationCommand.PropagateResults(reader);

await npgsqlReader.NextResultAsync(cancellationToken).ConfigureAwait(false);

commandIndex++;
}
}
catch (DbUpdateException)
{
throw;
}
catch (Exception ex)
catch (Exception ex) when (ex is not DbUpdateException and not OperationCanceledException)
{
throw new DbUpdateException(
RelationalStrings.UpdateStoreException,
ex,
ModificationCommands[commandIndex].Entries);
}
}

private IReadOnlyList<IUpdateEntry> AggregateEntries(int endIndex, int commandCount)
{
var entries = new List<IUpdateEntry>();
for (var i = endIndex - commandCount; i < endIndex; i++)
{
entries.AddRange(ModificationCommands[i].Entries);
}

return entries;
}

/// <summary>
/// Throws an exception indicating the command affected an unexpected number of rows.
/// </summary>
/// <param name="reader">The data reader.</param>
/// <param name="commandIndex">The ordinal of the command.</param>
/// <param name="expectedRowsAffected">The expected number of rows affected.</param>
/// <param name="rowsAffected">The actual number of rows affected.</param>
protected virtual void ThrowAggregateUpdateConcurrencyException(
RelationalDataReader reader,
int commandIndex,
int expectedRowsAffected,
int rowsAffected)
{
var entries = AggregateEntries(commandIndex + 1, expectedRowsAffected);
var exception = new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(expectedRowsAffected, rowsAffected),
entries);

if (!Dependencies.UpdateLogger.OptimisticConcurrencyException(
Dependencies.CurrentContext.Context,
entries,
exception,
(c, ex, e, d) => CreateConcurrencyExceptionEventData(c, reader, ex, e, d)).IsSuppressed)
{
throw exception;
}
}

/// <summary>
/// Throws an exception indicating the command affected an unexpected number of rows.
/// </summary>
/// <param name="reader">The data reader.</param>
/// <param name="commandIndex">The ordinal of the command.</param>
/// <param name="expectedRowsAffected">The expected number of rows affected.</param>
/// <param name="rowsAffected">The actual number of rows affected.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
/// <returns> A task that represents the asynchronous operation.</returns>
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
protected virtual async Task ThrowAggregateUpdateConcurrencyExceptionAsync(
RelationalDataReader reader,
int commandIndex,
int expectedRowsAffected,
int rowsAffected,
CancellationToken cancellationToken)
{
var entries = AggregateEntries(commandIndex + 1, expectedRowsAffected);
var exception = new DbUpdateConcurrencyException(
RelationalStrings.UpdateConcurrencyException(expectedRowsAffected, rowsAffected),
entries);

if (!(await Dependencies.UpdateLogger.OptimisticConcurrencyExceptionAsync(
Dependencies.CurrentContext.Context,
entries,
exception,
(c, ex, e, d) => CreateConcurrencyExceptionEventData(c, reader, ex, e, d),
cancellationToken: cancellationToken)
.ConfigureAwait(false)).IsSuppressed)
{
throw exception;
}
}

private static RelationalConcurrencyExceptionEventData CreateConcurrencyExceptionEventData(
DbContext context,
RelationalDataReader reader,
DbUpdateConcurrencyException exception,
IReadOnlyList<IUpdateEntry> entries,
EventDefinition<Exception> definition)
=> new(
definition,
(definition1, payload)
=> ((EventDefinition<Exception>)definition1).GenerateMessage(((ConcurrencyExceptionEventData)payload).Exception),
context,
reader.RelationalConnection.DbConnection,
reader.DbCommand,
reader.DbDataReader,
reader.CommandId,
reader.RelationalConnection.ConnectionId,
entries,
exception);
}
14 changes: 14 additions & 0 deletions test/EFCore.PG.FunctionalTests/EntitySplittingNpgsqlTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;

namespace Npgsql.EntityFrameworkCore.PostgreSQL;

public class EntitySplittingNpgsqlTest : EntitySplittingTestBase
{
public EntitySplittingNpgsqlTest(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
}

protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
}
20 changes: 4 additions & 16 deletions test/EFCore.PG.FunctionalTests/FindNpgsqlTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ public FindNpgsqlTestSet(FindNpgsqlFixture fixture)
{
}

protected override TEntity Find<TEntity>(DbContext context, params object[] keyValues)
=> context.Set<TEntity>().Find(keyValues);

protected override ValueTask<TEntity> FindAsync<TEntity>(DbContext context, params object[] keyValues)
=> context.Set<TEntity>().FindAsync(keyValues);
protected override TestFinder Finder { get; } = new FindViaSetFinder();
}

public class FindNpgsqlTestContext : FindNpgsqlTest
Expand All @@ -31,11 +27,7 @@ public FindNpgsqlTestContext(FindNpgsqlFixture fixture)
{
}

protected override TEntity Find<TEntity>(DbContext context, params object[] keyValues)
=> context.Find<TEntity>(keyValues);

protected override ValueTask<TEntity> FindAsync<TEntity>(DbContext context, params object[] keyValues)
=> context.FindAsync<TEntity>(keyValues);
protected override TestFinder Finder { get; } = new FindViaContextFinder();
}

public class FindNpgsqlTestNonGeneric : FindNpgsqlTest
Expand All @@ -45,16 +37,12 @@ public FindNpgsqlTestNonGeneric(FindNpgsqlFixture fixture)
{
}

protected override TEntity Find<TEntity>(DbContext context, params object[] keyValues)
=> (TEntity)context.Find(typeof(TEntity), keyValues);

protected override async ValueTask<TEntity> FindAsync<TEntity>(DbContext context, params object[] keyValues)
=> (TEntity)await context.FindAsync(typeof(TEntity), keyValues);
protected override TestFinder Finder { get; } = new FindViaNonGenericContextFinder();
}

public class FindNpgsqlFixture : FindFixtureBase
{
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService<ILoggerFactory>();
protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;

namespace Npgsql.EntityFrameworkCore.PostgreSQL;

public abstract class QueryExpressionInterceptionNpgsqlTestBase : QueryExpressionInterceptionTestBase
{
protected QueryExpressionInterceptionNpgsqlTestBase(InterceptionNpgsqlFixtureBase fixture)
: base(fixture)
{
}

public abstract class InterceptionNpgsqlFixtureBase : InterceptionFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;

protected override IServiceCollection InjectInterceptors(
IServiceCollection serviceCollection,
IEnumerable<IInterceptor> injectedInterceptors)
=> base.InjectInterceptors(serviceCollection.AddEntityFrameworkNpgsql(), injectedInterceptors);

public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
new NpgsqlDbContextOptionsBuilder(base.AddOptions(builder))
.ExecutionStrategy(d => new NpgsqlExecutionStrategy(d));
return builder;
}
}

public class QueryExpressionInterceptionNpgsqlTest
: QueryExpressionInterceptionNpgsqlTestBase, IClassFixture<QueryExpressionInterceptionNpgsqlTest.InterceptionNpgsqlFixture>
{
public QueryExpressionInterceptionNpgsqlTest(InterceptionNpgsqlFixture fixture)
: base(fixture)
{
}

public class InterceptionNpgsqlFixture : InterceptionNpgsqlFixtureBase
{
protected override string StoreName
=> "QueryExpressionInterception";

protected override bool ShouldSubscribeToDiagnosticListener
=> false;
}
}

public class QueryExpressionInterceptionWithDiagnosticsNpgsqlTest
: QueryExpressionInterceptionNpgsqlTestBase,
IClassFixture<QueryExpressionInterceptionWithDiagnosticsNpgsqlTest.InterceptionNpgsqlFixture>
{
public QueryExpressionInterceptionWithDiagnosticsNpgsqlTest(InterceptionNpgsqlFixture fixture)
: base(fixture)
{
}

public class InterceptionNpgsqlFixture : InterceptionNpgsqlFixtureBase
{
protected override string StoreName
=> "QueryExpressionInterceptionWithDiagnostics";

protected override bool ShouldSubscribeToDiagnosticListener
=> true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ protected SaveChangesInterceptionNpgsqlTestBase(InterceptionNpgsqlFixtureBase fi

public abstract class InterceptionNpgsqlFixtureBase : InterceptionFixtureBase
{
protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance;
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;

protected override IServiceCollection InjectInterceptors(
IServiceCollection serviceCollection,
IEnumerable<IInterceptor> injectedInterceptors)
=> base.InjectInterceptors(serviceCollection.AddEntityFrameworkNpgsql(), injectedInterceptors);

public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
new NpgsqlDbContextOptionsBuilder(base.AddOptions(builder))
.ExecutionStrategy(d => new NpgsqlExecutionStrategy(d));
return builder;
}
}

public class SaveChangesInterceptionNpgsqlTest
Expand Down Expand Up @@ -67,4 +75,4 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
}
}
}
}
}
4 changes: 2 additions & 2 deletions test/EFCore.PG.FunctionalTests/UpdatesNpgsqlTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ public override void Identifiers_are_generated_correctly()
entityType2.GetKeys().Single().GetName());
Assert.Equal(
"ExtraPropertyWithAnExtremelyLongAndOverlyConvolutedNameThatIsU~",
entityType2.GetProperties().ElementAt(1).GetColumnName());
entityType2.GetProperties().ElementAt(1).GetColumnName(StoreObjectIdentifier.Table(entityType2.GetTableName())));
Assert.Equal(
"ExtraPropertyWithAnExtremelyLongAndOverlyConvolutedNameThatIs~1",
entityType2.GetProperties().ElementAt(2).GetColumnName());
entityType2.GetProperties().ElementAt(2).GetColumnName(StoreObjectIdentifier.Table(entityType2.GetTableName())));
Assert.Equal(
"IX_LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameT~1",
entityType2.GetIndexes().Single().GetDatabaseName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,9 @@ public void GenerateCodeLiteral_returns_Interval_array_literal()

[Fact]
public void GenerateCodeLiteral_returns_Interval_list_literal()
=> Assert.Throws<NotSupportedException>(
() => CodeLiteral(new List<Interval>
=> Assert.Equal(
"new List<Interval> { new NodaTime.Interval(NodaTime.Instant.FromUnixTimeTicks(8923875980000000L), NodaTime.Instant.FromUnixTimeTicks(8923947980000000L)), new NodaTime.Interval(NodaTime.Instant.FromUnixTimeTicks(8924739980000000L), NodaTime.Instant.FromUnixTimeTicks(8924811980000000L)) }",
CodeLiteral(new List<Interval>
{
new(
new LocalDateTime(1998, 4, 12, 13, 26, 38).InUtc().ToInstant(),
Expand Down Expand Up @@ -440,8 +441,9 @@ public void GenerateCodeLiteral_returns_DateInterval_array_literal()

[Fact]
public void GenerateCodeLiteral_returns_DateInterval_list_literal()
=> Assert.Throws<NotSupportedException>(
() => CodeLiteral(new List<DateInterval>
=> Assert.Equal(
"new List<DateInterval> { new NodaTime.DateInterval(new NodaTime.LocalDate(2002, 3, 4), new NodaTime.LocalDate(2002, 3, 5)), new NodaTime.DateInterval(new NodaTime.LocalDate(2002, 3, 8), new NodaTime.LocalDate(2002, 3, 10)) }",
CodeLiteral(new List<DateInterval>
{
new(new(2002, 3, 4), new(2002, 3, 5)),
new(new(2002, 3, 8), new(2002, 3, 10))
Expand Down
Loading

0 comments on commit 037cffd

Please sign in to comment.