未验证 提交 037cffd0 编写于 作者: S Shay Rojansky 提交者: GitHub

Sync EF Core to 7.0.0-preview.7.22359.1 (#2422)

上级 ba7d2a58
<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>
......
......@@ -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
}
......@@ -77,13 +74,11 @@ 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");
......@@ -91,13 +86,11 @@ protected override void Consume(RelationalDataReader reader)
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,
......@@ -138,10 +131,8 @@ 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
);
await ThrowAggregateUpdateConcurrencyExceptionAsync(reader, commandIndex, 1, 0, cancellationToken)
.ConfigureAwait(false);
}
#pragma warning restore 618
}
......@@ -154,14 +145,12 @@ protected override void Consume(RelationalDataReader reader)
// 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");
......@@ -169,13 +158,11 @@ protected override void Consume(RelationalDataReader reader)
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,
......@@ -183,4 +170,96 @@ protected override void Consume(RelationalDataReader reader)
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);
}
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;
}
......@@ -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
......@@ -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
......@@ -45,11 +37,7 @@ 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
......@@ -57,4 +45,4 @@ public class FindNpgsqlFixture : FindFixtureBase
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ServiceProvider.GetRequiredService<ILoggerFactory>();
protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance;
}
}
\ No newline at end of file
}
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;
}
}
}
......@@ -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
......@@ -67,4 +75,4 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
}
}
}
}
\ No newline at end of file
}
......@@ -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());
......
......@@ -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(),
......@@ -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))
......
......@@ -707,10 +707,16 @@ public void GenerateCodeLiteral_returns_multirange_array_literal()
}
[Fact]
public void GenerateCodeLiteral_does_not_support_multirange_lists()
public void GenerateCodeLiteral_returns_multirange_list_literal()
{
var value = new List<NpgsqlRange<int>>();
Assert.Throws<NotSupportedException>(() => CodeLiteral(value));
var value = new List<NpgsqlRange<int>>
{
new(4, 7),
new(9, lowerBoundIsInclusive: true, 10, upperBoundIsInclusive: false),
new(13, lowerBoundIsInclusive: false, lowerBoundInfinite: false, default, upperBoundIsInclusive: false, upperBoundInfinite: true)
};
var literal = CodeLiteral(value);
Assert.Equal("new List<NpgsqlRange<int>> { new NpgsqlTypes.NpgsqlRange<int>(4, 7), new NpgsqlTypes.NpgsqlRange<int>(9, true, 10, false), new NpgsqlTypes.NpgsqlRange<int>(13, false, false, 0, false, true) }", literal);
}
[Fact]
......
......@@ -244,12 +244,35 @@ public class FakeRelationalCommandDiagnosticsLogger
TimeSpan duration)
=> default;
public InterceptionResult DataReaderClosing(
IRelationalConnection connection,
DbCommand command,
DbDataReader dataReader,
Guid commandId,
int recordsAffected,
int readCount,
DateTimeOffset startTime)
=> default;
public ValueTask<InterceptionResult> DataReaderClosingAsync(
IRelationalConnection connection,
DbCommand command,
DbDataReader dataReader,
Guid commandId,
int recordsAffected,
int readCount,
DateTimeOffset startTime)
=> default;
public bool ShouldLogCommandCreate(DateTimeOffset now)
=> true;
public bool ShouldLogCommandExecute(DateTimeOffset now)
=> true;
public bool ShouldLogDataReaderClose(DateTimeOffset now)
=> true;
public bool ShouldLogDataReaderDispose(DateTimeOffset now)
=> true;
}
......@@ -22,8 +22,6 @@ public void Uses_MaxBatchSize_specified_in_NpgsqlOptionsExtension()
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
var factory = new NpgsqlModificationCommandBatchFactory(
new ModificationCommandBatchFactoryDependencies(
new RelationalCommandBuilderFactory(
......@@ -38,7 +36,8 @@ public void Uses_MaxBatchSize_specified_in_NpgsqlOptionsExtension()
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new CurrentDbContext(new FakeDbContext()),
logger),
new FakeRelationalCommandDiagnosticsLogger(),
new FakeDiagnosticsLogger<DbLoggerCategory.Update>()),
optionsBuilder.Options);
var batch = factory.Create();
......@@ -59,8 +58,6 @@ public void MaxBatchSize_is_optional()
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
var factory = new NpgsqlModificationCommandBatchFactory(
new ModificationCommandBatchFactoryDependencies(
new RelationalCommandBuilderFactory(
......@@ -75,7 +72,8 @@ public void MaxBatchSize_is_optional()
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new CurrentDbContext(new FakeDbContext()),
logger),
new FakeRelationalCommandDiagnosticsLogger(),
new FakeDiagnosticsLogger<DbLoggerCategory.Update>()),
optionsBuilder.Options);
var batch = factory.Create();
......
......@@ -20,8 +20,6 @@ public void AddCommand_returns_false_when_max_batch_size_is_reached()
new NpgsqlSqlGenerationHelper(new RelationalSqlGenerationHelperDependencies()),
new NpgsqlSingletonOptions());
var logger = new FakeRelationalCommandDiagnosticsLogger();
var batch = new NpgsqlModificationCommandBatch(
new ModificationCommandBatchFactoryDependencies(
new RelationalCommandBuilderFactory(
......@@ -36,7 +34,8 @@ public void AddCommand_returns_false_when_max_batch_size_is_reached()
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new CurrentDbContext(new FakeDbContext()),
logger),
new FakeRelationalCommandDiagnosticsLogger(),
new FakeDiagnosticsLogger<DbLoggerCategory.Update>()),
maxBatchSize: 1);
Assert.True(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册