diff --git a/azure-pipelines-public.yml b/azure-pipelines-public.yml
index cd521dd30b6..d56ef44140b 100644
--- a/azure-pipelines-public.yml
+++ b/azure-pipelines-public.yml
@@ -53,7 +53,7 @@ stages:
enablePublishTestResults: true
pool:
name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
+ demands: ImageOverride -equals 1es-windows-2022-open
timeoutInMinutes: 90
variables:
- _InternalBuildArgs: ''
@@ -143,7 +143,7 @@ stages:
timeoutInMinutes: 180
pool:
name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
+ demands: ImageOverride -equals 1es-windows-2022-open
variables:
# Rely on task Arcade injects, not auto-injected build step.
- skipComponentGovernanceDetection: true
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 996ae4981cd..aec5517a8fb 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,71 +1,71 @@
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/runtime
- 6f23d04dc2b2039e9eaf97bee2ac02a77ce56b21
+ d0f3235d312f7cf9683012b3fe96b2c6f20a1743
-
+
https://github.com/dotnet/arcade
- 91599268652b51969b8d8088d4f2f2ba7b3ebb19
+ d21db44e84b9038ea7b2add139adee2303d46800
-
+
https://github.com/dotnet/arcade
- 91599268652b51969b8d8088d4f2f2ba7b3ebb19
+ d21db44e84b9038ea7b2add139adee2303d46800
-
+
https://github.com/dotnet/arcade
- 91599268652b51969b8d8088d4f2f2ba7b3ebb19
+ d21db44e84b9038ea7b2add139adee2303d46800
diff --git a/eng/Versions.props b/eng/Versions.props
index 2cb258c3402..5350252095a 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -16,21 +16,21 @@
False
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
- 9.0.0-rc.2.24423.10
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
+ 9.0.0-rc.2.24429.19
- 9.0.0-beta.24423.2
+ 9.0.0-beta.24429.5
17.9.5
@@ -39,7 +39,7 @@
4.8.0
1.1.2-beta1.24121.1
- 1.11.3
+ 1.12.0
1.3.2
1.8.1
diff --git a/global.json b/global.json
index 2dfd6cb7cf1..108c2c01e2f 100644
--- a/global.json
+++ b/global.json
@@ -13,7 +13,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24423.2",
- "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24423.2"
+ "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24429.5",
+ "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24429.5"
}
}
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
index 0340619574b..130041d556d 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
@@ -84,17 +84,16 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
{
// If the entity type is not a keyed, root document in the container, then it doesn't have an `id` mapping, so
// undo anything that was done by previous execution of this convention.
- if (jsonIdProperty != null)
+ if (jsonIdProperty is not null)
{
jsonIdProperty.Builder.ToJsonProperty(null);
- entityType.Builder.HasNoProperty(jsonIdProperty);
+ entityType.Builder.RemoveUnusedImplicitProperties([jsonIdProperty]);
}
- if (computedIdProperty != null
+ if (computedIdProperty is not null
&& computedIdProperty != jsonIdProperty)
{
- entityType.Builder.HasNoProperty(computedIdProperty);
- }
+ entityType.Builder.RemoveUnusedImplicitProperties([computedIdProperty]); }
return;
}
@@ -115,10 +114,17 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
// - IDiscriminatorPropertySetConvention
// - IEntityTypeBaseTypeChangedConvention
var idDefinition = DefinitionFactory.Create((IEntityType)entityType)!;
- var keyProperty = (IConventionProperty?)idDefinition.Properties.FirstOrDefault();
if (idDefinition is { IncludesDiscriminator: false, Properties.Count: 1 })
{
- var clrType = keyProperty!.GetValueConverter()?.ProviderClrType ?? keyProperty.ClrType;
+ // If the property maps to a string in the JSON document, then we can use it directly, even if a value converter
+ // is applied. On the other hand, if it maps to a numeric or bool, then we need to duplicate this to preserve the
+ // non-string value for queries.
+ var keyProperty = (IConventionProperty)idDefinition.Properties.First();
+ var mapping = Dependencies.TypeMappingSource.FindMapping((IProperty)keyProperty);
+ var clrType = mapping?.Converter?.ProviderClrType
+ ?? mapping?.ClrType
+ ?? keyProperty!.ClrType;
+
if (clrType == typeof(string))
{
// We are at the point where we are going to map the `id` directly to the PK.
diff --git a/src/EFCore.Design/EFCore.Design.csproj b/src/EFCore.Design/EFCore.Design.csproj
index 586c8a32753..cfb2ac7b165 100644
--- a/src/EFCore.Design/EFCore.Design.csproj
+++ b/src/EFCore.Design/EFCore.Design.csproj
@@ -62,7 +62,7 @@
-
+
diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
index 291952bca9d..d5efd7f6a81 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
@@ -443,7 +443,8 @@ protected virtual void GenerateProperty(
var clrType = (FindValueConverter(property)?.ProviderClrType ?? property.ClrType)
.MakeNullable(property.IsNullable);
- var propertyBuilderName = $"{entityTypeBuilderName}.Property<{Code.Reference(clrType)}>({Code.Literal(property.Name)})";
+ var propertyCall = property.IsPrimitiveCollection ? "PrimitiveCollection" : "Property";
+ var propertyBuilderName = $"{entityTypeBuilderName}.{propertyCall}<{Code.Reference(clrType)}>({Code.Literal(property.Name)})";
stringBuilder
.AppendLine()
diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
index cabde564e20..c05c73c46a3 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
@@ -80,6 +80,7 @@ private enum Id
PendingModelChangesWarning,
NonTransactionalMigrationOperationWarning,
AcquiringMigrationLock,
+ MigrationsUserTransactionWarning,
// Query events
QueryClientEvaluationWarning = CoreEventId.RelationalBaseId + 500,
@@ -764,6 +765,19 @@ private static EventId MakeMigrationsId(Id id)
///
public static readonly EventId AcquiringMigrationLock = MakeMigrationsId(Id.AcquiringMigrationLock);
+ ///
+ /// A migration lock is being acquired.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId MigrationsUserTransactionWarning = MakeMigrationsId(Id.MigrationsUserTransactionWarning);
+
private static readonly string _queryPrefix = DbLoggerCategory.Query.Name + ".";
private static EventId MakeQueryId(Id id)
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
index a8afec569e1..177b7e90bba 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
@@ -2425,6 +2425,36 @@ private static string AcquiringMigrationLock(EventDefinitionBase definition, Eve
return d.GenerateMessage();
}
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ public static void MigrationsUserTransactionWarning(
+ this IDiagnosticsLogger diagnostics)
+ {
+ var definition = RelationalResources.LogMigrationsUserTransaction(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new EventData(
+ definition,
+ MigrationsUserTransactionWarning);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string MigrationsUserTransactionWarning(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ return d.GenerateMessage();
+ }
+
///
/// Logs for the event.
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
index 9b57a07998e..762172c8944 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
@@ -367,6 +367,15 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase? LogAcquiringMigrationLock;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase? LogMigrationsUserTransactionWarning;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.Relational/Migrations/HistoryRepository.cs b/src/EFCore.Relational/Migrations/HistoryRepository.cs
index 78dc3b24013..bfb565786c0 100644
--- a/src/EFCore.Relational/Migrations/HistoryRepository.cs
+++ b/src/EFCore.Relational/Migrations/HistoryRepository.cs
@@ -47,6 +47,14 @@ protected HistoryRepository(HistoryRepositoryDependencies dependencies)
TableSchema = relationalOptions.MigrationsHistoryTableSchema;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public abstract LockReleaseBehavior LockReleaseBehavior { get; }
+
///
/// Relational provider-specific dependencies for this service.
///
@@ -120,14 +128,15 @@ protected virtual string ProductVersionColumnName
///
/// if the table already exists, otherwise.
public virtual bool Exists()
- => InterpretExistsResult(
- Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalar(
- new RelationalCommandParameterObject(
- Dependencies.Connection,
- null,
- null,
- Dependencies.CurrentContext.Context,
- Dependencies.CommandLogger, CommandSource.Migrations)));
+ => Dependencies.DatabaseCreator.Exists()
+ && InterpretExistsResult(
+ Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalar(
+ new RelationalCommandParameterObject(
+ Dependencies.Connection,
+ null,
+ null,
+ Dependencies.CurrentContext.Context,
+ Dependencies.CommandLogger, CommandSource.Migrations)));
///
/// Checks whether or not the history table exists.
@@ -139,15 +148,16 @@ public virtual bool Exists()
///
/// If the is canceled.
public virtual async Task ExistsAsync(CancellationToken cancellationToken = default)
- => InterpretExistsResult(
- await Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalarAsync(
- new RelationalCommandParameterObject(
- Dependencies.Connection,
- null,
- null,
- Dependencies.CurrentContext.Context,
- Dependencies.CommandLogger, CommandSource.Migrations),
- cancellationToken).ConfigureAwait(false));
+ => await Dependencies.DatabaseCreator.ExistsAsync(cancellationToken).ConfigureAwait(false)
+ && InterpretExistsResult(
+ await Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalarAsync(
+ new RelationalCommandParameterObject(
+ Dependencies.Connection,
+ null,
+ null,
+ Dependencies.CurrentContext.Context,
+ Dependencies.CommandLogger, CommandSource.Migrations),
+ cancellationToken).ConfigureAwait(false));
///
/// Interprets the result of executing .
@@ -173,13 +183,15 @@ public virtual string GetCreateScript()
/// Creates the history table.
///
public virtual void Create()
- => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(GetCreateCommands(), Dependencies.Connection);
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(
+ GetCreateCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true);
///
/// Creates the history table.
///
public virtual Task CreateAsync(CancellationToken cancellationToken = default)
- => Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(GetCreateCommands(), Dependencies.Connection, cancellationToken);
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
+ GetCreateCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken);
///
/// Returns the migration commands that will create the history table.
@@ -194,19 +206,37 @@ protected virtual IReadOnlyList GetCreateCommands()
return commandList;
}
+ bool IHistoryRepository.CreateIfNotExists()
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(
+ GetCreateIfNotExistsCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true)
+ != 0;
+
+ async Task IHistoryRepository.CreateIfNotExistsAsync(CancellationToken cancellationToken)
+ => (await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
+ GetCreateIfNotExistsCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken).ConfigureAwait(false))
+ != 0;
+
+ private IReadOnlyList GetCreateIfNotExistsCommands()
+ => Dependencies.MigrationsSqlGenerator.Generate([new SqlOperation
+ {
+ Sql = GetCreateIfNotExistsScript(),
+ SuppressTransaction = true
+ }]);
+
///
/// Gets an exclusive lock on the database.
///
/// An object that can be disposed to release the lock.
- public abstract IDisposable GetDatabaseLock();
+ public abstract IMigrationsDatabaseLock AcquireDatabaseLock();
///
/// Gets an exclusive lock on the database.
///
/// A to observe while waiting for the task to complete.
+ ///
/// An object that can be disposed to release the lock.
/// If the is canceled.
- public abstract Task GetDatabaseLockAsync(CancellationToken cancellationToken = default);
+ public abstract Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default);
///
/// Configures the entity type mapped to the history table.
diff --git a/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs b/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
index b5f40bd8ba2..a8bf28d3f22 100644
--- a/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
+++ b/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
@@ -59,7 +59,8 @@ public HistoryRepositoryDependencies(
IRelationalTypeMappingSource typeMappingSource,
ICurrentDbContext currentContext,
IModelRuntimeInitializer modelRuntimeInitializer,
- IRelationalCommandDiagnosticsLogger commandLogger)
+ IRelationalCommandDiagnosticsLogger commandLogger,
+ IDiagnosticsLogger migrationsLogger)
{
DatabaseCreator = databaseCreator;
RawSqlCommandBuilder = rawSqlCommandBuilder;
@@ -75,6 +76,7 @@ public HistoryRepositoryDependencies(
CurrentContext = currentContext;
ModelRuntimeInitializer = modelRuntimeInitializer;
CommandLogger = commandLogger;
+ MigrationsLogger = migrationsLogger;
}
///
@@ -146,4 +148,9 @@ public HistoryRepositoryDependencies(
/// The command logger
///
public IRelationalCommandDiagnosticsLogger CommandLogger { get; init; }
+
+ ///
+ /// The migrations logger
+ ///
+ public IDiagnosticsLogger MigrationsLogger { get; init; }
}
diff --git a/src/EFCore.Relational/Migrations/IHistoryRepository.cs b/src/EFCore.Relational/Migrations/IHistoryRepository.cs
index 7bf3a461650..2189e6cea87 100644
--- a/src/EFCore.Relational/Migrations/IHistoryRepository.cs
+++ b/src/EFCore.Relational/Migrations/IHistoryRepository.cs
@@ -50,12 +50,44 @@ public interface IHistoryRepository
///
/// A to observe while waiting for the task to complete.
///
- /// A task that represents the asynchronous operation. The task result contains
- /// if the table already exists, otherwise.
+ /// A task that represents the asynchronous operation.
///
/// If the is canceled.
Task CreateAsync(CancellationToken cancellationToken = default);
+ ///
+ /// Creates the history table if it doesn't exist.
+ ///
+ /// if the table was created, otherwise.
+ bool CreateIfNotExists()
+ {
+ if (!Exists())
+ {
+ Create();
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Creates the history table.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous operation. The task result contains
+ /// if the table was created, otherwise.
+ ///
+ /// If the is canceled.
+ async Task CreateIfNotExistsAsync(CancellationToken cancellationToken = default)
+ {
+ if (!await ExistsAsync(cancellationToken).ConfigureAwait(false))
+ {
+ await CreateAsync(cancellationToken).ConfigureAwait(false);
+ return true;
+ }
+ return false;
+ }
+
///
/// Queries the history table for all migrations that have been applied.
///
@@ -75,18 +107,23 @@ Task> GetAppliedMigrationsAsync(
CancellationToken cancellationToken = default);
///
- /// Gets an exclusive lock on the database.
+ /// The condition under witch the lock is released implicitly.
+ ///
+ LockReleaseBehavior LockReleaseBehavior { get; }
+
+ ///
+ /// Acquires an exclusive lock on the database.
///
/// An object that can be disposed to release the lock.
- IDisposable GetDatabaseLock();
+ IMigrationsDatabaseLock AcquireDatabaseLock();
///
- /// Gets an exclusive lock on the database.
+ /// Acquires an exclusive lock on the database asynchronously.
///
/// A to observe while waiting for the task to complete.
/// An object that can be disposed to release the lock.
/// If the is canceled.
- Task GetDatabaseLockAsync(CancellationToken cancellationToken = default);
+ Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default);
///
/// Generates a SQL script that will create the history table.
diff --git a/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs b/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
index 7069ba9c045..039bbaa4f15 100644
--- a/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
+++ b/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
+
namespace Microsoft.EntityFrameworkCore.Migrations;
///
@@ -28,6 +30,24 @@ void ExecuteNonQuery(
IEnumerable migrationCommands,
IRelationalConnection connection);
+ ///
+ /// Executes the given commands using the given database connection.
+ ///
+ /// The commands to execute.
+ /// The connection to use.
+ /// The state of the current migration execution.
+ ///
+ /// Indicates whether the transaction started by this call should be commited.
+ /// If , the transaction will be made available in .
+ ///
+ /// The isolation level for the transaction.
+ int ExecuteNonQuery(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ IsolationLevel? isolationLevel = null);
+
///
/// Executes the given commands using the given database connection.
///
@@ -40,4 +60,26 @@ Task ExecuteNonQueryAsync(
IEnumerable migrationCommands,
IRelationalConnection connection,
CancellationToken cancellationToken = default);
+
+ ///
+ /// Executes the given commands using the given database connection.
+ ///
+ /// The commands to execute.
+ /// The connection to use.
+ /// The state of the current migration execution.
+ ///
+ /// Indicates whether the transaction started by this call should be commited.
+ /// If , the transaction will be made available in .
+ ///
+ /// The isolation level for the transaction.
+ /// A to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation.
+ /// If the is canceled.
+ Task ExecuteNonQueryAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ IsolationLevel? isolationLevel = null,
+ CancellationToken cancellationToken = default);
}
diff --git a/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs b/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs
new file mode 100644
index 00000000000..b08ea1b9faa
--- /dev/null
+++ b/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Migrations;
+
+///
+/// Represents an exclusive lock on the database that is used to ensure that only one migration application can be run at a time.
+///
+///
+/// Typically only database providers implement this.
+///
+public interface IMigrationsDatabaseLock : IDisposable, IAsyncDisposable
+{
+ ///
+ /// The history repository.
+ ///
+ protected IHistoryRepository HistoryRepository { get; }
+
+ ///
+ /// Acquires an exclusive lock on the database again if the current one was already released.
+ ///
+ /// Indicates whether the connection was reopened.
+ ///
+ /// Indicates whether the transaction was restarted.
+ /// if there's no current transaction.
+ ///
+ /// An object that can be disposed to release the lock.
+ IMigrationsDatabaseLock ReacquireIfNeeded(bool connectionReopened, bool? transactionRestarted)
+ {
+ if ((connectionReopened && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Connection)
+ || (transactionRestarted is true && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Transaction))
+ {
+ Dispose();
+ return HistoryRepository.AcquireDatabaseLock();
+ }
+
+ return this;
+ }
+
+ ///
+ /// Acquires an exclusive lock on the database again, if the current one was already released.
+ ///
+ /// Indicates whether the connection was reopened.
+ ///
+ /// Indicates whether the transaction was restarted.
+ /// if there's no current transaction.
+ ///
+ /// A to observe while waiting for the task to complete.
+ /// An object that can be disposed to release the lock.
+ async Task ReacquireIfNeededAsync(
+ bool connectionReopened, bool? transactionRestarted, CancellationToken cancellationToken = default)
+ {
+ if ((connectionReopened && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Connection)
+ || (transactionRestarted is true && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Transaction))
+ {
+ await DisposeAsync().ConfigureAwait(false);
+ return await HistoryRepository.AcquireDatabaseLockAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ return this;
+ }
+}
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs b/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
index 4b2d78a85bc..68c6ba4a886 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
@@ -28,74 +28,120 @@ public class MigrationCommandExecutor(IExecutionStrategy executionStrategy) : IM
public virtual void ExecuteNonQuery(
IEnumerable migrationCommands,
IRelationalConnection connection)
+ => ExecuteNonQuery(
+ migrationCommands.ToList(), connection, new MigrationExecutionState(), commitTransaction: true);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual int ExecuteNonQuery(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel = null)
{
- // TODO: Remove ToList, see #19710
- var commands = migrationCommands.ToList();
- var userTransaction = connection.CurrentTransaction;
- if (userTransaction is not null
- && (commands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
+ var inUserTransaction = connection.CurrentTransaction is not null && executionState.Transaction == null;
+ if (inUserTransaction
+ && (migrationCommands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
{
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
}
- using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
- {
- var parameters = new ExecuteParameters(commands, connection);
- if (userTransaction is null)
- {
- executionStrategy.Execute(parameters, static (_, p) => Execute(p, beginTransaction: true), verifySucceeded: null);
- }
- else
- {
- Execute(parameters, beginTransaction: false);
- }
- }
+ using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
+
+ return executionStrategy.Execute(
+ (migrationCommands, connection, inUserTransaction, executionState, commitTransaction, isolationLevel),
+ static (_, s) => Execute(
+ s.migrationCommands,
+ s.connection,
+ s.executionState,
+ beginTransaction: !s.inUserTransaction,
+ commitTransaction: !s.inUserTransaction && s.commitTransaction,
+ s.isolationLevel),
+ verifySucceeded: null);
}
- private static bool Execute(ExecuteParameters parameters, bool beginTransaction)
+ private static int Execute(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool beginTransaction,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel)
{
- var migrationCommands = parameters.MigrationCommands;
- var connection = parameters.Connection;
- IDbContextTransaction? transaction = null;
- connection.Open();
+ var result = 0;
+ var connectionOpened = connection.Open();
+ Check.DebugAssert(!connectionOpened || executionState.Transaction == null,
+ "executionState.Transaction should be null");
+
try
{
- for (var i = parameters.CurrentCommandIndex; i < migrationCommands.Count; i++)
+ for (var i = executionState.LastCommittedCommandIndex; i < migrationCommands.Count; i++)
{
var command = migrationCommands[i];
- if (transaction == null
+ if (executionState.Transaction == null
&& !command.TransactionSuppressed
&& beginTransaction)
{
- transaction = connection.BeginTransaction();
+ executionState.Transaction = isolationLevel == null
+ ? connection.BeginTransaction()
+ : connection.BeginTransaction(isolationLevel.Value);
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = executionState.DatabaseLock.ReacquireIfNeeded(
+ connectionOpened, transactionRestarted: true);
+ connectionOpened = false;
+ }
}
- if (transaction != null
+ if (executionState.Transaction != null
&& command.TransactionSuppressed)
{
- transaction.Commit();
- transaction.Dispose();
- transaction = null;
- parameters.CurrentCommandIndex = i;
+ executionState.Transaction.Commit();
+ executionState.Transaction.Dispose();
+ executionState.Transaction = null;
+ executionState.LastCommittedCommandIndex = i;
+ executionState.AnyOperationPerformed = true;
+
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = executionState.DatabaseLock.ReacquireIfNeeded(
+ connectionOpened, transactionRestarted: null);
+ connectionOpened = false;
+ }
}
- command.ExecuteNonQuery(connection);
+ result = command.ExecuteNonQuery(connection);
- if (transaction == null)
+ if (executionState.Transaction == null)
{
- parameters.CurrentCommandIndex = i + 1;
+ executionState.LastCommittedCommandIndex = i + 1;
+ executionState.AnyOperationPerformed = true;
}
}
- transaction?.Commit();
+ if (commitTransaction
+ && executionState.Transaction != null)
+ {
+ executionState.Transaction.Commit();
+ executionState.Transaction.Dispose();
+ executionState.Transaction = null;
+ }
}
- finally
+ catch
{
- transaction?.Dispose();
+ executionState.Transaction?.Dispose();
+ executionState.Transaction = null;
connection.Close();
+ throw;
}
- return true;
+ connection.Close();
+ return result;
}
///
@@ -104,95 +150,136 @@ private static bool Execute(ExecuteParameters parameters, bool beginTransaction)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual async Task ExecuteNonQueryAsync(
+ public virtual Task ExecuteNonQueryAsync(
IEnumerable migrationCommands,
IRelationalConnection connection,
CancellationToken cancellationToken = default)
+ => ExecuteNonQueryAsync(
+ migrationCommands.ToList(), connection, new MigrationExecutionState(), commitTransaction: true, System.Data.IsolationLevel.Unspecified, cancellationToken);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual async Task ExecuteNonQueryAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel = null,
+ CancellationToken cancellationToken = default)
{
- var commands = migrationCommands.ToList();
- var userTransaction = connection.CurrentTransaction;
- if (userTransaction is not null
- && (commands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
+ var inUserTransaction = connection.CurrentTransaction is not null && executionState.Transaction == null;
+ if (inUserTransaction
+ && (migrationCommands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
{
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
}
using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
- var parameters = new ExecuteParameters(commands, connection);
- if (userTransaction is null)
- {
- await executionStrategy.ExecuteAsync(
- parameters,
- static (_, p, ct) => ExecuteAsync(p, beginTransaction: true, ct),
- verifySucceeded: null,
- cancellationToken).ConfigureAwait(false);
- }
- else
- {
- await ExecuteAsync(parameters, beginTransaction: false, cancellationToken).ConfigureAwait(false);
- }
+ return await executionStrategy.ExecuteAsync(
+ (migrationCommands, connection, inUserTransaction, executionState, commitTransaction, isolationLevel),
+ static (_, s, ct) => ExecuteAsync(
+ s.migrationCommands,
+ s.connection,
+ s.executionState,
+ beginTransaction: !s.inUserTransaction,
+ commitTransaction: !s.inUserTransaction && s.commitTransaction,
+ s.isolationLevel,
+ ct),
+ verifySucceeded: null,
+ cancellationToken).ConfigureAwait(false);
}
- private static async Task ExecuteAsync(ExecuteParameters parameters, bool beginTransaction, CancellationToken cancellationToken)
+ private static async Task ExecuteAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool beginTransaction,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel,
+ CancellationToken cancellationToken)
{
- var migrationCommands = parameters.MigrationCommands;
- var connection = parameters.Connection;
- IDbContextTransaction? transaction = null;
- await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ var result = 0;
+ var connectionOpened = await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ Check.DebugAssert(!connectionOpened || executionState.Transaction == null,
+ "executionState.Transaction should be null");
+
try
{
- for (var i = parameters.CurrentCommandIndex; i < migrationCommands.Count; i++)
+ for (var i = executionState.LastCommittedCommandIndex; i < migrationCommands.Count; i++)
{
+ var lockReacquired = false;
var command = migrationCommands[i];
- if (transaction == null
+ if (executionState.Transaction == null
&& !command.TransactionSuppressed
&& beginTransaction)
{
- transaction = await connection.BeginTransactionAsync(cancellationToken)
+ executionState.Transaction = await (isolationLevel == null
+ ? connection.BeginTransactionAsync(cancellationToken)
+ : connection.BeginTransactionAsync(isolationLevel.Value, cancellationToken))
.ConfigureAwait(false);
+
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = await executionState.DatabaseLock.ReacquireIfNeededAsync(
+ connectionOpened, transactionRestarted: true, cancellationToken)
+ .ConfigureAwait(false);
+ lockReacquired = true;
+ }
}
- if (transaction != null
+ if (executionState.Transaction != null
&& command.TransactionSuppressed)
{
- await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
- await transaction.DisposeAsync().ConfigureAwait(false);
- transaction = null;
- parameters.CurrentCommandIndex = i;
+ await executionState.Transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
+ executionState.LastCommittedCommandIndex = i;
+ executionState.AnyOperationPerformed = true;
+
+ if (executionState.DatabaseLock != null
+ && !lockReacquired)
+ {
+ executionState.DatabaseLock = await executionState.DatabaseLock.ReacquireIfNeededAsync(
+ connectionOpened, transactionRestarted: null, cancellationToken)
+ .ConfigureAwait(false);
+ }
}
- await command.ExecuteNonQueryAsync(connection, cancellationToken: cancellationToken)
+ result = await command.ExecuteNonQueryAsync(connection, cancellationToken: cancellationToken)
.ConfigureAwait(false);
- if (transaction == null)
+ if (executionState.Transaction == null)
{
- parameters.CurrentCommandIndex = i + 1;
+ executionState.LastCommittedCommandIndex = i + 1;
+ executionState.AnyOperationPerformed = true;
}
}
- if (transaction != null)
+ if (commitTransaction
+ && executionState.Transaction != null)
{
- await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
}
}
- finally
+ catch
{
- if (transaction != null)
+ if (executionState.Transaction != null)
{
- await transaction.DisposeAsync().ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
}
-
await connection.CloseAsync().ConfigureAwait(false);
+ throw;
}
- return true;
- }
-
- private sealed class ExecuteParameters(List migrationCommands, IRelationalConnection connection)
- {
- public int CurrentCommandIndex;
- public List MigrationCommands { get; } = migrationCommands;
- public IRelationalConnection Connection { get; } = connection;
+ await connection.CloseAsync().ConfigureAwait(false);
+ return result;
}
}
diff --git a/src/EFCore.Relational/Migrations/Internal/Migrator.cs b/src/EFCore.Relational/Migrations/Internal/Migrator.cs
index c10cdd105c0..a6644ef6fc1 100644
--- a/src/EFCore.Relational/Migrations/Internal/Migrator.cs
+++ b/src/EFCore.Relational/Migrations/Internal/Migrator.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Transactions;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
namespace Microsoft.EntityFrameworkCore.Migrations.Internal;
@@ -29,6 +30,7 @@ public class Migrator : IMigrator
private readonly IDesignTimeModel _designTimeModel;
private readonly string _activeProvider;
private readonly IDbContextOptions _contextOptions;
+ private readonly IExecutionStrategy _executionStrategy;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -52,7 +54,8 @@ public Migrator(
IDatabaseProvider databaseProvider,
IMigrationsModelDiffer migrationsModelDiffer,
IDesignTimeModel designTimeModel,
- IDbContextOptions contextOptions)
+ IDbContextOptions contextOptions,
+ IExecutionStrategy executionStrategy)
{
_migrationsAssembly = migrationsAssembly;
_historyRepository = historyRepository;
@@ -70,8 +73,17 @@ public Migrator(
_designTimeModel = designTimeModel;
_activeProvider = databaseProvider.Name;
_contextOptions = contextOptions;
+ _executionStrategy = executionStrategy;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual System.Data.IsolationLevel? MigrationTransactionIsolationLevel => null;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -80,29 +92,90 @@ public Migrator(
///
public virtual void Migrate(string? targetMigration)
{
+ var useTransaction = _connection.CurrentTransaction is null;
+ if (!useTransaction
+ && _executionStrategy.RetriesOnFailure)
+ {
+ throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
+ }
+
if (RelationalResources.LogPendingModelChanges(_logger).WarningBehavior != WarningBehavior.Ignore
&& HasPendingModelChanges())
{
_logger.PendingModelChangesWarning(_currentContext.Context.GetType());
}
+ if (!useTransaction)
+ {
+ _logger.MigrationsUserTransactionWarning();
+ }
+
_logger.MigrateUsingConnection(this, _connection);
+ using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
+
if (!_databaseCreator.Exists())
{
_databaseCreator.Create();
}
+ _connection.Open();
try
{
- _connection.Open();
+ var state = new MigrationExecutionState();
+ if (_historyRepository.LockReleaseBehavior != LockReleaseBehavior.Transaction
+ && useTransaction)
+ {
+ state.DatabaseLock = _historyRepository.AcquireDatabaseLock();
+ }
- _logger.AcquiringMigrationLock();
- using var _ = _historyRepository.GetDatabaseLock();
+ _executionStrategy.Execute(
+ this,
+ static (_, migrator) =>
+ {
+ migrator._connection.Open();
+ try
+ {
+ return migrator._historyRepository.CreateIfNotExists();
+ }
+ finally
+ {
+ migrator._connection.Close();
+ }
+ },
+ verifySucceeded: null);
+
+ _executionStrategy.Execute(
+ (Migrator: this,
+ TargetMigration: targetMigration,
+ State: state,
+ UseTransaction: useTransaction),
+ static (c, s) => s.Migrator.MigrateImplementation(c, s.TargetMigration, s.State, s.UseTransaction),
+ static (_, s) => new ExecutionResult(
+ successful: s.Migrator.VerifyMigrationSucceeded(s.TargetMigration, s.State),
+ result: true));
+ }
+ finally
+ {
+ _connection.Close();
+ }
+ }
- if (!_historyRepository.Exists())
+ private bool MigrateImplementation(
+ DbContext context, string? targetMigration, MigrationExecutionState state, bool useTransaction)
+ {
+ var connectionOpened = _connection.Open();
+ try
+ {
+ if (useTransaction)
{
- _historyRepository.Create();
+ state.Transaction = MigrationTransactionIsolationLevel == null
+ ? _connection.BeginTransaction()
+ : _connection.BeginTransaction(MigrationTransactionIsolationLevel.Value);
+
+ state.DatabaseLock = state.DatabaseLock == null
+ ? _historyRepository.AcquireDatabaseLock()
+ : state.DatabaseLock.ReacquireIfNeeded(connectionOpened, useTransaction);
}
PopulateMigrations(
@@ -113,7 +186,15 @@ public virtual void Migrate(string? targetMigration)
var commandLists = GetMigrationCommandLists(migratorData);
foreach (var commandList in commandLists)
{
- _migrationCommandExecutor.ExecuteNonQuery(commandList(), _connection);
+ var (id, getCommands) = commandList;
+ if (id != state.CurrentMigrationId)
+ {
+ state.CurrentMigrationId = id;
+ state.LastCommittedCommandIndex = 0;
+ }
+
+ _migrationCommandExecutor.ExecuteNonQuery(
+ getCommands(), _connection, state, commitTransaction: false, MigrationTransactionIsolationLevel);
}
var coreOptionsExtension =
@@ -123,20 +204,22 @@ public virtual void Migrate(string? targetMigration)
var seed = coreOptionsExtension.Seeder;
if (seed != null)
{
- var context = _currentContext.Context;
- var operationsPerformed = migratorData.AppliedMigrations.Count != 0
- || migratorData.RevertedMigrations.Count != 0;
- using var transaction = context.Database.BeginTransaction();
- seed(context, operationsPerformed);
- transaction.Commit();
+ seed(context, state.AnyOperationPerformed);
}
else if (coreOptionsExtension.AsyncSeeder != null)
{
throw new InvalidOperationException(CoreStrings.MissingSeeder);
}
+
+ state.Transaction?.Commit();
+ return state.AnyOperationPerformed;
}
finally
{
+ state.DatabaseLock?.Dispose();
+ state.DatabaseLock = null;
+ state.Transaction?.Dispose();
+ state.Transaction = null;
_connection.Close();
}
}
@@ -151,30 +234,96 @@ public virtual async Task MigrateAsync(
string? targetMigration,
CancellationToken cancellationToken = default)
{
+ var useTransaction = _connection.CurrentTransaction is null;
+ if (!useTransaction
+ && _executionStrategy.RetriesOnFailure)
+ {
+ throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
+ }
+
if (RelationalResources.LogPendingModelChanges(_logger).WarningBehavior != WarningBehavior.Ignore
&& HasPendingModelChanges())
{
_logger.PendingModelChangesWarning(_currentContext.Context.GetType());
}
+ if (!useTransaction)
+ {
+ _logger.MigrationsUserTransactionWarning();
+ }
+
_logger.MigrateUsingConnection(this, _connection);
+ using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
+
if (!await _databaseCreator.ExistsAsync(cancellationToken).ConfigureAwait(false))
{
await _databaseCreator.CreateAsync(cancellationToken).ConfigureAwait(false);
}
+ await _connection.OpenAsync(cancellationToken).ConfigureAwait(false);
try
{
- await _connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ var state = new MigrationExecutionState();
+ if (_historyRepository.LockReleaseBehavior != LockReleaseBehavior.Transaction
+ && useTransaction)
+ {
+ state.DatabaseLock = await _historyRepository.AcquireDatabaseLockAsync(cancellationToken).ConfigureAwait(false);
+ }
- _logger.AcquiringMigrationLock();
- var dbLock = await _historyRepository.GetDatabaseLockAsync(cancellationToken).ConfigureAwait(false);
- await using var _ = dbLock.ConfigureAwait(false);
+ await _executionStrategy.ExecuteAsync(
+ this,
+ static async (_, migrator, ct) =>
+ {
+ await migrator._connection.OpenAsync(ct).ConfigureAwait(false);
+ try
+ {
+ return await migrator._historyRepository.CreateIfNotExistsAsync(ct).ConfigureAwait(false);
+ }
+ finally
+ {
+ await migrator._connection.CloseAsync().ConfigureAwait(false);
+ }
+ },
+ verifySucceeded: null,
+ cancellationToken).ConfigureAwait(false);
+
+ await _executionStrategy.ExecuteAsync(
+ (Migrator: this,
+ TargetMigration: targetMigration,
+ State: state,
+ UseTransaction: useTransaction),
+ async static (c, s, ct) => await s.Migrator.MigrateImplementationAsync(
+ c, s.TargetMigration, s.State, s.UseTransaction, ct).ConfigureAwait(false),
+ async static (_, s, ct) => new ExecutionResult(
+ successful: await s.Migrator.VerifyMigrationSucceededAsync(s.TargetMigration, s.State, ct).ConfigureAwait(false),
+ result: true),
+ cancellationToken)
+ .ConfigureAwait(false);
+ }
+ finally
+ {
+ await _connection.CloseAsync().ConfigureAwait(false);
+ }
+ }
- if (!await _historyRepository.ExistsAsync(cancellationToken).ConfigureAwait(false))
+ private async Task MigrateImplementationAsync(
+ DbContext context, string? targetMigration, MigrationExecutionState state, bool useTransaction, CancellationToken cancellationToken = default)
+ {
+ var connectionOpened = await _connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ if (useTransaction)
{
- await _historyRepository.CreateAsync(cancellationToken).ConfigureAwait(false);
+ state.Transaction = await (MigrationTransactionIsolationLevel == null
+ ? context.Database.BeginTransactionAsync(cancellationToken)
+ : context.Database.BeginTransactionAsync(MigrationTransactionIsolationLevel.Value, cancellationToken))
+ .ConfigureAwait(false);
+
+ state.DatabaseLock = state.DatabaseLock == null
+ ? await _historyRepository.AcquireDatabaseLockAsync(cancellationToken).ConfigureAwait(false)
+ : await state.DatabaseLock.ReacquireIfNeededAsync(connectionOpened, useTransaction, cancellationToken)
+ .ConfigureAwait(false);
}
PopulateMigrations(
@@ -185,7 +334,15 @@ public virtual async Task MigrateAsync(
var commandLists = GetMigrationCommandLists(migratorData);
foreach (var commandList in commandLists)
{
- await _migrationCommandExecutor.ExecuteNonQueryAsync(commandList(), _connection, cancellationToken)
+ var (id, getCommands) = commandList;
+ if (id != state.CurrentMigrationId)
+ {
+ state.CurrentMigrationId = id;
+ state.LastCommittedCommandIndex = 0;
+ }
+
+ await _migrationCommandExecutor.ExecuteNonQueryAsync(
+ getCommands(), _connection, state, commitTransaction: false, MigrationTransactionIsolationLevel, cancellationToken)
.ConfigureAwait(false);
}
@@ -196,26 +353,36 @@ await _migrationCommandExecutor.ExecuteNonQueryAsync(commandList(), _connection,
var seedAsync = coreOptionsExtension.AsyncSeeder;
if (seedAsync != null)
{
- var context = _currentContext.Context;
- var operationsPerformed = migratorData.AppliedMigrations.Count != 0
- || migratorData.RevertedMigrations.Count != 0;
- var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
- await using var __ = transaction.ConfigureAwait(false);
- await seedAsync(context, operationsPerformed, cancellationToken).ConfigureAwait(false);
- await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await seedAsync(context, state.AnyOperationPerformed, cancellationToken).ConfigureAwait(false);
}
else if (coreOptionsExtension.Seeder != null)
{
throw new InvalidOperationException(CoreStrings.MissingSeeder);
}
+
+ if (state.Transaction != null)
+ {
+ await state.Transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ }
+ return state.AnyOperationPerformed;
}
finally
{
- _connection.Close();
+ if (state.DatabaseLock != null)
+ {
+ state.DatabaseLock.Dispose();
+ state.DatabaseLock = null;
+ }
+ if (state.Transaction != null)
+ {
+ await state.Transaction.DisposeAsync().ConfigureAwait(false);
+ state.Transaction = null;
+ }
+ await _connection.CloseAsync().ConfigureAwait(false);
}
}
- private IEnumerable>> GetMigrationCommandLists(MigratorData parameters)
+ private IEnumerable<(string, Func>)> GetMigrationCommandLists(MigratorData parameters)
{
var migrationsToApply = parameters.AppliedMigrations;
var migrationsToRevert = parameters.RevertedMigrations;
@@ -226,7 +393,7 @@ private IEnumerable>> GetMigrationCommandLi
var migration = migrationsToRevert[i];
var index = i;
- yield return () =>
+ yield return (migration.GetId(), () =>
{
_logger.MigrationReverting(this, migration);
@@ -242,12 +409,12 @@ private IEnumerable>> GetMigrationCommandLi
}
return commands;
- };
+ });
}
foreach (var migration in migrationsToApply)
{
- yield return () =>
+ yield return (migration.GetId(), () =>
{
_logger.MigrationApplying(this, migration);
@@ -259,7 +426,7 @@ private IEnumerable>> GetMigrationCommandLi
}
return commands;
- };
+ });
}
if (migrationsToRevert.Count + migrationsToApply.Count == 0)
@@ -340,6 +507,26 @@ protected virtual void PopulateMigrations(
parameters = new MigratorData(migrationsToApply, migrationsToRevert, actualTargetMigration);
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual bool VerifyMigrationSucceeded(
+ string? targetMigration, MigrationExecutionState state)
+ => false;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual Task VerifyMigrationSucceededAsync(
+ string? targetMigration, MigrationExecutionState state, CancellationToken cancellationToken)
+ => Task.FromResult(false);
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.Relational/Migrations/LockReleaseBehavior.cs b/src/EFCore.Relational/Migrations/LockReleaseBehavior.cs
new file mode 100644
index 00000000000..bd1f96e995c
--- /dev/null
+++ b/src/EFCore.Relational/Migrations/LockReleaseBehavior.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Migrations;
+
+///
+/// Represents the conditions under which a lock is released implicitly.
+///
+public enum LockReleaseBehavior
+{
+ ///
+ /// The lock is released when the transaction is committed or rolled back.
+ ///
+ Transaction,
+
+ ///
+ /// The lock is released when the connection is closed.
+ ///
+ Connection,
+
+ ///
+ /// The lock can only be released explicitly.
+ ///
+ Explicit
+}
diff --git a/src/EFCore.Relational/Migrations/MigrationExecutionState.cs b/src/EFCore.Relational/Migrations/MigrationExecutionState.cs
new file mode 100644
index 00000000000..accd83e0cfc
--- /dev/null
+++ b/src/EFCore.Relational/Migrations/MigrationExecutionState.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Migrations;
+
+///
+/// Contains the state of the current migration execution.
+///
+public sealed class MigrationExecutionState
+{
+ ///
+ /// The index of the last command that was committed to the database.
+ ///
+ public int LastCommittedCommandIndex { get; set; }
+
+ ///
+ /// The id the migration that is currently being applied.
+ ///
+ public string? CurrentMigrationId { get; set; }
+
+ ///
+ /// Indicates whether any migration operation was performed.
+ ///
+ public bool AnyOperationPerformed { get; set; }
+
+ ///
+ /// The database lock that is in use.
+ ///
+ public IMigrationsDatabaseLock? DatabaseLock { get; set; }
+
+ ///
+ /// The transaction that is in use.
+ ///
+ public IDbContextTransaction? Transaction { get; set; }
+}
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index c2350480259..731c62c4ec2 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -2130,7 +2130,7 @@ public static string UnsupportedOperatorForSqlExpression(object? nodeType, objec
nodeType, expressionType);
///
- /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
+ /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
///
public static string UnsupportedPropertyType(object? entity, object? property, object? clrType)
=> string.Format(
@@ -2256,7 +2256,7 @@ private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.EntityFrameworkCore.Properties.RelationalStrings", typeof(RelationalResources).Assembly);
///
- /// Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations for more information if this takes too long.
+ /// Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
///
public static EventDefinition LogAcquiringMigrationLock(IDiagnosticsLogger logger)
{
@@ -3424,6 +3424,31 @@ public static EventDefinition LogMigrationAttributeMissingWarning(IDiagn
return (EventDefinition)definition;
}
+ ///
+ /// A transaction was started before applying migrations. This prevents a database lock to be acquired and hence the database will not be protected from concurrent migration applications. The transactions and execution strategy are already managed by EF as needed. Remove the external transaction.
+ ///
+ public static EventDefinition LogMigrationsUserTransaction(IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogMigrationsUserTransactionWarning;
+ if (definition == null)
+ {
+ definition = NonCapturingLazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogMigrationsUserTransactionWarning,
+ logger,
+ static logger => new EventDefinition(
+ logger.Options,
+ RelationalEventId.MigrationsUserTransactionWarning,
+ LogLevel.Warning,
+ "RelationalEventId.MigrationsUserTransactionWarning",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.MigrationsUserTransactionWarning,
+ _resourceManager.GetString("LogMigrationsUserTransaction")!)));
+ }
+
+ return (EventDefinition)definition;
+ }
+
///
/// Compiling a query which loads related collections for more than one collection navigation, either via 'Include' or through projection, but no 'QuerySplittingBehavior' has been configured. By default, Entity Framework will use 'QuerySplittingBehavior.SingleQuery', which can potentially result in slow query performance. See https://go.microsoft.com/fwlink/?linkid=2134277 for more information. To identify the query that's triggering this warning call 'ConfigureWarnings(w => w.Throw(RelationalEventId.MultipleCollectionIncludeWarning))'.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index d6390b417fc..104daf9dafb 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -1,17 +1,17 @@
-
@@ -786,6 +786,10 @@
A [Migration] attribute isn't specified on the '{class}' class.
Warning RelationalEventId.MigrationAttributeMissingWarning string
+
+ A transaction was started before applying migrations. This prevents a database lock to be acquired and hence the database will not be protected from concurrent migration applications. The transactions and execution strategy are already managed by EF as needed. Remove the external transaction.
+ Warning RelationalEventId.MigrationsUserTransactionWarning
+
Compiling a query which loads related collections for more than one collection navigation, either via 'Include' or through projection, but no 'QuerySplittingBehavior' has been configured. By default, Entity Framework will use 'QuerySplittingBehavior.SingleQuery', which can potentially result in slow query performance. See https://go.microsoft.com/fwlink/?linkid=2134277 for more information. To identify the query that's triggering this warning call 'ConfigureWarnings(w => w.Throw(RelationalEventId.MultipleCollectionIncludeWarning))'.
Warning RelationalEventId.MultipleCollectionIncludeWarning
diff --git a/src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs b/src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs
index ad6a2bcd7df..96f5ca0f76a 100644
--- a/src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs
+++ b/src/EFCore.Relational/Storage/RelationalDatabaseCreator.cs
@@ -116,7 +116,7 @@ public virtual Task DeleteAsync(CancellationToken cancellationToken = default)
/// to incrementally update the schema. It is assumed that none of the tables exist in the database.
///
public virtual void CreateTables()
- => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(GetCreateTablesCommands(), Dependencies.Connection);
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(GetCreateTablesCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true);
///
/// Asynchronously creates all tables for the current model in the database. No attempt is made
@@ -129,7 +129,7 @@ public virtual void CreateTables()
/// If the is canceled.
public virtual Task CreateTablesAsync(CancellationToken cancellationToken = default)
=> Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
- GetCreateTablesCommands(), Dependencies.Connection, cancellationToken);
+ GetCreateTablesCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken);
///
/// Gets the commands that will create all tables from the model.
diff --git a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
index 875567bd1b7..01e6918c559 100644
--- a/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
+++ b/src/EFCore.SqlServer/Design/Internal/SqlServerAnnotationCodeGenerator.cs
@@ -55,18 +55,42 @@ private static readonly MethodInfo PropertyIsSparseMethodInfo
= typeof(SqlServerPropertyBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerPropertyBuilderExtensions.IsSparse), [typeof(PropertyBuilder), typeof(bool)])!;
+ private static readonly MethodInfo PrimitiveCollectionIsSparseMethodInfo
+ = typeof(SqlServerPrimitiveCollectionBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerPrimitiveCollectionBuilderExtensions.IsSparse), [typeof(PrimitiveCollectionBuilder), typeof(bool)])!;
+
+ private static readonly MethodInfo ComplexTypePropertyIsSparseMethodInfo
+ = typeof(SqlServerComplexTypePropertyBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerComplexTypePropertyBuilderExtensions.IsSparse), [typeof(ComplexTypePropertyBuilder), typeof(bool)])!;
+
+ private static readonly MethodInfo ComplexTypePrimitiveCollectionIsSparseMethodInfo
+ = typeof(SqlServerComplexTypePrimitiveCollectionBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerComplexTypePrimitiveCollectionBuilderExtensions.IsSparse), [typeof(ComplexTypePrimitiveCollectionBuilder), typeof(bool)])!;
+
private static readonly MethodInfo PropertyUseIdentityColumnsMethodInfo
= typeof(SqlServerPropertyBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerPropertyBuilderExtensions.UseIdentityColumn), [typeof(PropertyBuilder), typeof(long), typeof(int)])!;
+ private static readonly MethodInfo ComplexTypePropertyUseIdentityColumnsMethodInfo
+ = typeof(SqlServerComplexTypePropertyBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerComplexTypePropertyBuilderExtensions.UseIdentityColumn), [typeof(ComplexTypePropertyBuilder), typeof(long), typeof(int)])!;
+
private static readonly MethodInfo PropertyUseHiLoMethodInfo
= typeof(SqlServerPropertyBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerPropertyBuilderExtensions.UseHiLo), [typeof(PropertyBuilder), typeof(string), typeof(string)])!;
+ private static readonly MethodInfo ComplexTypePropertyUseHiLoMethodInfo
+ = typeof(SqlServerComplexTypePropertyBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerComplexTypePropertyBuilderExtensions.UseHiLo), [typeof(ComplexTypePropertyBuilder), typeof(string), typeof(string)])!;
+
private static readonly MethodInfo PropertyUseSequenceMethodInfo
= typeof(SqlServerPropertyBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerPropertyBuilderExtensions.UseSequence), [typeof(PropertyBuilder), typeof(string), typeof(string)])!;
+ private static readonly MethodInfo ComplexTypePropertyUseSequenceMethodInfo
+ = typeof(SqlServerComplexTypePropertyBuilderExtensions).GetRuntimeMethod(
+ nameof(SqlServerComplexTypePropertyBuilderExtensions.UseSequence), [typeof(ComplexTypePropertyBuilder), typeof(string), typeof(string)])!;
+
private static readonly MethodInfo IndexIsClusteredMethodInfo
= typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerIndexBuilderExtensions.IsClustered), [typeof(IndexBuilder), typeof(bool)])!;
@@ -144,7 +168,7 @@ public override IReadOnlyList GenerateFluentApiCalls(
{
var fragments = new List(base.GenerateFluentApiCalls(model, annotations));
- if (GenerateValueGenerationStrategy(annotations, model, onModel: true) is MethodCallCodeFragment valueGenerationStrategy)
+ if (GenerateValueGenerationStrategy(annotations, model, onModel: true, complexType: false) is MethodCallCodeFragment valueGenerationStrategy)
{
fragments.Add(valueGenerationStrategy);
}
@@ -179,7 +203,9 @@ public override IReadOnlyList GenerateFluentApiCalls(
{
var fragments = new List(base.GenerateFluentApiCalls(property, annotations));
- if (GenerateValueGenerationStrategy(annotations, property.DeclaringType.Model, onModel: false) is MethodCallCodeFragment
+ var isPrimitiveCollection = property.IsPrimitiveCollection;
+
+ if (GenerateValueGenerationStrategy(annotations, property.DeclaringType.Model, onModel: false, complexType: property.DeclaringType is IComplexType) is MethodCallCodeFragment
valueGenerationStrategy)
{
fragments.Add(valueGenerationStrategy);
@@ -187,10 +213,17 @@ public override IReadOnlyList GenerateFluentApiCalls(
if (GetAndRemove(annotations, SqlServerAnnotationNames.Sparse) is bool isSparse)
{
+ var methodInfo = isPrimitiveCollection
+ ? property.DeclaringType is IComplexType
+ ? ComplexTypePrimitiveCollectionIsSparseMethodInfo
+ : PrimitiveCollectionIsSparseMethodInfo
+ : property.DeclaringType is IComplexType
+ ? ComplexTypePropertyIsSparseMethodInfo
+ : PropertyIsSparseMethodInfo;
fragments.Add(
isSparse
- ? new MethodCallCodeFragment(PropertyIsSparseMethodInfo)
- : new MethodCallCodeFragment(PropertyIsSparseMethodInfo, false));
+ ? new MethodCallCodeFragment(methodInfo)
+ : new MethodCallCodeFragment(methodInfo, false));
}
return fragments;
@@ -367,7 +400,8 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an
private static MethodCallCodeFragment? GenerateValueGenerationStrategy(
IDictionary annotations,
IModel model,
- bool onModel)
+ bool onModel,
+ bool complexType)
{
SqlServerValueGenerationStrategy strategy;
if (annotations.TryGetValue(SqlServerAnnotationNames.ValueGenerationStrategy, out var strategyAnnotation)
@@ -405,7 +439,11 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an
?? model.FindAnnotation(SqlServerAnnotationNames.IdentityIncrement)?.Value as int?
?? 1;
return new MethodCallCodeFragment(
- onModel ? ModelUseIdentityColumnsMethodInfo : PropertyUseIdentityColumnsMethodInfo,
+ onModel
+ ? ModelUseIdentityColumnsMethodInfo
+ : complexType
+ ? ComplexTypePropertyUseIdentityColumnsMethodInfo
+ : PropertyUseIdentityColumnsMethodInfo,
(seed, increment) switch
{
(1L, 1) => [],
@@ -418,7 +456,11 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an
var name = GetAndRemove(annotations, SqlServerAnnotationNames.HiLoSequenceName);
var schema = GetAndRemove(annotations, SqlServerAnnotationNames.HiLoSequenceSchema);
return new MethodCallCodeFragment(
- onModel ? ModelUseHiLoMethodInfo : PropertyUseHiLoMethodInfo,
+ onModel
+ ? ModelUseHiLoMethodInfo
+ : complexType
+ ? ComplexTypePropertyUseHiLoMethodInfo
+ : PropertyUseHiLoMethodInfo,
(name, schema) switch
{
(null, null) => [],
@@ -435,7 +477,11 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an
var schema = GetAndRemove(annotations, SqlServerAnnotationNames.SequenceSchema);
return new MethodCallCodeFragment(
- onModel ? ModelUseKeySequencesMethodInfo : PropertyUseSequenceMethodInfo,
+ onModel
+ ? ModelUseKeySequencesMethodInfo
+ : complexType
+ ? ComplexTypePropertyUseSequenceMethodInfo
+ : PropertyUseSequenceMethodInfo,
(name: nameOrSuffix, schema) switch
{
(null, null) => [],
diff --git a/src/EFCore.SqlServer/Migrations/Internal/SqlServerHistoryRepository.cs b/src/EFCore.SqlServer/Migrations/Internal/SqlServerHistoryRepository.cs
index cc22d67fca4..202c7a2c131 100644
--- a/src/EFCore.SqlServer/Migrations/Internal/SqlServerHistoryRepository.cs
+++ b/src/EFCore.SqlServer/Migrations/Internal/SqlServerHistoryRepository.cs
@@ -59,8 +59,18 @@ protected override bool InterpretExistsResult(object? value)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public override IDisposable GetDatabaseLock()
+ public override LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Connection;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override IMigrationsDatabaseLock AcquireDatabaseLock()
{
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
var dbLock = CreateMigrationDatabaseLock();
int result;
try
@@ -91,8 +101,10 @@ public override IDisposable GetDatabaseLock()
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public override async Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public override async Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
{
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
var dbLock = CreateMigrationDatabaseLock();
int result;
try
@@ -135,7 +147,8 @@ private SqlServerMigrationDatabaseLock CreateMigrationDatabaseLock()
EXEC @result = sp_releaseapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session';
SELECT @result
"""),
- CreateRelationalCommandParameters());
+ CreateRelationalCommandParameters(),
+ this);
private RelationalCommandParameterObject CreateRelationalCommandParameters()
=> new(
diff --git a/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationDatabaseLock.cs b/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationDatabaseLock.cs
index c102f6f9705..b31ff900625 100644
--- a/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationDatabaseLock.cs
+++ b/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationDatabaseLock.cs
@@ -16,11 +16,20 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class SqlServerMigrationDatabaseLock(
- IRelationalCommand relationalCommand,
+ IRelationalCommand releaseLockCommand,
RelationalCommandParameterObject relationalCommandParameters,
+ IHistoryRepository historyRepository,
CancellationToken cancellationToken = default)
- : IDisposable, IAsyncDisposable
+ : IMigrationsDatabaseLock
{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IHistoryRepository HistoryRepository => historyRepository;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -28,7 +37,7 @@ public class SqlServerMigrationDatabaseLock(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public void Dispose()
- => relationalCommand.ExecuteScalar(relationalCommandParameters);
+ => releaseLockCommand.ExecuteScalar(relationalCommandParameters);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -37,5 +46,5 @@ public void Dispose()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public async ValueTask DisposeAsync()
- => await relationalCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
+ => await releaseLockCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
index 589440790a1..e3f3fcdb838 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
@@ -14,26 +14,19 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class SqlServerDatabaseCreator : RelationalDatabaseCreator
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class SqlServerDatabaseCreator(
+ RelationalDatabaseCreatorDependencies dependencies,
+ ISqlServerConnection connection,
+ IRawSqlCommandBuilder rawSqlCommandBuilder) : RelationalDatabaseCreator(dependencies)
{
- private readonly ISqlServerConnection _connection;
- private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public SqlServerDatabaseCreator(
- RelationalDatabaseCreatorDependencies dependencies,
- ISqlServerConnection connection,
- IRawSqlCommandBuilder rawSqlCommandBuilder)
- : base(dependencies)
- {
- _connection = connection;
- _rawSqlCommandBuilder = rawSqlCommandBuilder;
- }
+ private readonly ISqlServerConnection _connection = connection;
+ private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder = rawSqlCommandBuilder;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -62,7 +55,7 @@ public override void Create()
using (var masterConnection = _connection.CreateMasterConnection())
{
Dependencies.MigrationCommandExecutor
- .ExecuteNonQuery(CreateCreateOperations(), masterConnection);
+ .ExecuteNonQuery(CreateCreateOperations(), masterConnection, new MigrationExecutionState(), commitTransaction: true);
ClearPool();
}
@@ -82,7 +75,7 @@ public override async Task CreateAsync(CancellationToken cancellationToken = def
await using (masterConnection.ConfigureAwait(false))
{
await Dependencies.MigrationCommandExecutor
- .ExecuteNonQueryAsync(CreateCreateOperations(), masterConnection, cancellationToken)
+ .ExecuteNonQueryAsync(CreateCreateOperations(), masterConnection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken)
.ConfigureAwait(false);
ClearPool();
@@ -157,8 +150,7 @@ private IReadOnlyList CreateCreateOperations()
{
var builder = new SqlConnectionStringBuilder(_connection.DbConnection.ConnectionString);
return Dependencies.MigrationsSqlGenerator.Generate(
- new[]
- {
+ [
new SqlServerCreateDatabaseOperation
{
Name = builder.InitialCatalog,
@@ -166,7 +158,7 @@ private IReadOnlyList CreateCreateOperations()
Collation = Dependencies.CurrentContext.Context.GetService()
.Model.GetRelationalModel().Collation
}
- });
+ ]);
}
///
@@ -345,7 +337,7 @@ public override void Delete()
using var masterConnection = _connection.CreateMasterConnection();
Dependencies.MigrationCommandExecutor
- .ExecuteNonQuery(CreateDropCommands(), masterConnection);
+ .ExecuteNonQuery(CreateDropCommands(), masterConnection, new MigrationExecutionState(), commitTransaction: true);
}
///
@@ -361,7 +353,7 @@ public override async Task DeleteAsync(CancellationToken cancellationToken = def
var masterConnection = _connection.CreateMasterConnection();
await using var _ = masterConnection.ConfigureAwait(false);
await Dependencies.MigrationCommandExecutor
- .ExecuteNonQueryAsync(CreateDropCommands(), masterConnection, cancellationToken)
+ .ExecuteNonQueryAsync(CreateDropCommands(), masterConnection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
diff --git a/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteHistoryRepository.cs b/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteHistoryRepository.cs
index e346fd1e698..89d8fefc0c8 100644
--- a/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteHistoryRepository.cs
+++ b/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteHistoryRepository.cs
@@ -103,11 +103,21 @@ public override string GetEndIfScript()
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public override IDisposable GetDatabaseLock()
+ public override LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override IMigrationsDatabaseLock AcquireDatabaseLock()
{
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
if (!InterpretExistsResult(
- Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
- .ExecuteScalar(CreateRelationalCommandParameters())))
+ Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
+ .ExecuteScalar(CreateRelationalCommandParameters())))
{
CreateLockTableCommand().ExecuteNonQuery(CreateRelationalCommandParameters());
}
@@ -129,8 +139,6 @@ public override IDisposable GetDatabaseLock()
retryDelay = retryDelay.Add(retryDelay);
}
}
-
- throw new TimeoutException();
}
///
@@ -139,11 +147,14 @@ public override IDisposable GetDatabaseLock()
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public override async Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public override async Task AcquireDatabaseLockAsync(
+ CancellationToken cancellationToken = default)
{
+ Dependencies.MigrationsLogger.AcquiringMigrationLock();
+
if (!InterpretExistsResult(
- await Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
- .ExecuteScalarAsync(CreateRelationalCommandParameters(), cancellationToken).ConfigureAwait(false)))
+ await Dependencies.RawSqlCommandBuilder.Build(CreateExistsSql(LockTableName))
+ .ExecuteScalarAsync(CreateRelationalCommandParameters(), cancellationToken).ConfigureAwait(false)))
{
await CreateLockTableCommand().ExecuteNonQueryAsync(CreateRelationalCommandParameters(), cancellationToken)
.ConfigureAwait(false);
@@ -167,8 +178,6 @@ await CreateLockTableCommand().ExecuteNonQueryAsync(CreateRelationalCommandParam
retryDelay = retryDelay.Add(retryDelay);
}
}
-
- throw new TimeoutException();
}
private IRelationalCommand CreateLockTableCommand()
@@ -206,7 +215,7 @@ DELETE FROM "{LockTableName}"
}
private SqliteMigrationDatabaseLock CreateMigrationDatabaseLock()
- => new(CreateDeleteLockCommand(), CreateRelationalCommandParameters());
+ => new(CreateDeleteLockCommand(), CreateRelationalCommandParameters(), this);
private RelationalCommandParameterObject CreateRelationalCommandParameters()
=> new(
diff --git a/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteMigrationDatabaseLock.cs b/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteMigrationDatabaseLock.cs
index 997cbcd4710..668d2107eaf 100644
--- a/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteMigrationDatabaseLock.cs
+++ b/src/EFCore.Sqlite.Core/Migrations/Internal/SqliteMigrationDatabaseLock.cs
@@ -10,11 +10,20 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class SqliteMigrationDatabaseLock(
- IRelationalCommand relationalCommand,
+ IRelationalCommand releaseLockCommand,
RelationalCommandParameterObject relationalCommandParameters,
+ IHistoryRepository historyRepository,
CancellationToken cancellationToken = default)
- : IDisposable, IAsyncDisposable
+ : IMigrationsDatabaseLock
{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IHistoryRepository HistoryRepository => historyRepository;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -22,7 +31,7 @@ public class SqliteMigrationDatabaseLock(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public void Dispose()
- => relationalCommand.ExecuteScalar(relationalCommandParameters);
+ => releaseLockCommand.ExecuteScalar(relationalCommandParameters);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -31,5 +40,5 @@ public void Dispose()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public async ValueTask DisposeAsync()
- => await relationalCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
+ => await releaseLockCommand.ExecuteScalarAsync(relationalCommandParameters, cancellationToken).ConfigureAwait(false);
}
diff --git a/src/EFCore.Tasks/buildTransitive/Microsoft.EntityFrameworkCore.Tasks.targets b/src/EFCore.Tasks/buildTransitive/Microsoft.EntityFrameworkCore.Tasks.targets
index 507ea1d8165..25463696b4c 100644
--- a/src/EFCore.Tasks/buildTransitive/Microsoft.EntityFrameworkCore.Tasks.targets
+++ b/src/EFCore.Tasks/buildTransitive/Microsoft.EntityFrameworkCore.Tasks.targets
@@ -114,15 +114,14 @@ For Publish:
-
-
-
+
+
+ Condition="Exists('$(EFGeneratedSourcesBuildFile)')">
+ Condition="Exists('$(EFGeneratedSourcesPublishFile)')">
@@ -185,12 +184,15 @@ For Publish:
@(_DebugSymbolsIntermediatePath);
$(NonExistentFile);
@(CustomAdditionalCompileOutputs)">
+
+
+
+
+
+
-
-
-
diff --git a/src/EFCore/Metadata/Conventions/ElementTypeChangedConvention.cs b/src/EFCore/Metadata/Conventions/ElementTypeChangedConvention.cs
index 83f9a04bccc..0fa4c791da8 100644
--- a/src/EFCore/Metadata/Conventions/ElementTypeChangedConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ElementTypeChangedConvention.cs
@@ -9,7 +9,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
/// See Model building conventions for more information and examples.
///
-public class ElementTypeChangedConvention : IPropertyElementTypeChangedConvention, IForeignKeyAddedConvention
+public class ElementTypeChangedConvention :
+ IPropertyElementTypeChangedConvention, IForeignKeyAddedConvention, IForeignKeyPropertiesChangedConvention
{
///
/// Creates a new instance of .
@@ -46,6 +47,22 @@ public void ProcessPropertyElementTypeChanged(
public void ProcessForeignKeyAdded(
IConventionForeignKeyBuilder foreignKeyBuilder,
IConventionContext context)
+ => ProcessForeignKey(foreignKeyBuilder);
+
+ ///
+ public void ProcessForeignKeyPropertiesChanged(
+ IConventionForeignKeyBuilder relationshipBuilder,
+ IReadOnlyList oldDependentProperties,
+ IConventionKey oldPrincipalKey,
+ IConventionContext> context)
+ {
+ if (relationshipBuilder.Metadata.IsInModel)
+ {
+ ProcessForeignKey(relationshipBuilder);
+ }
+ }
+
+ private static void ProcessForeignKey(IConventionForeignKeyBuilder foreignKeyBuilder)
{
var foreignKeyProperties = foreignKeyBuilder.Metadata.Properties;
var principalKeyProperties = foreignKeyBuilder.Metadata.PrincipalKey.Properties;
diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
index 3f9cc813662..2b5e128dbbb 100644
--- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
@@ -826,6 +826,12 @@ public virtual bool CanSetProviderValueComparer(
Metadata.SetValueConverter((Type?)null, configurationSource);
}
+ if (elementType == null
+ && CanSetConversion((Type?)null, configurationSource))
+ {
+ Metadata.RemoveAnnotation(CoreAnnotationNames.ValueConverter);
+ }
+
return new InternalElementTypeBuilder(Metadata.GetElementType()!, ModelBuilder);
}
diff --git a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
index f7d6c6dcb16..2183bbce984 100644
--- a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
@@ -205,6 +205,7 @@ public static bool IsCompatible(MemberInfo? newMemberInfo, PropertyBase existing
|| (memberInfo is PropertyInfo propertyInfo && propertyInfo.IsIndexerProperty()))
{
if (existingProperty.GetTypeConfigurationSource() is ConfigurationSource existingTypeConfigurationSource
+ && typeConfigurationSource != null
&& !typeConfigurationSource.Overrides(existingTypeConfigurationSource))
{
return null;
diff --git a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
index 4c08c77bc07..c4691b71fca 100644
--- a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
@@ -700,12 +700,12 @@ public async Task Entities_with_null_PK_can_be_added_with_normal_use_of_DbContex
var entry = await context.AddAsync(item);
- var id = entry.Property("__id").CurrentValue;
+ var id = entry.Property("Id").CurrentValue;
Assert.NotNull(item.Id);
Assert.NotNull(id);
- Assert.Equal($"{item.Id}", id);
+ Assert.Equal(item.Id, id);
Assert.Equal(EntityState.Added, entry.State);
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
index 9f8bb60182c..4d8994ef478 100644
--- a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
@@ -537,6 +537,186 @@ public virtual void No_alternate_key_is_created_if_id_is_hierarchical_partition_
Assert.DoesNotContain(entity.GetKeys(), k => k != entity.FindPrimaryKey());
}
+ [ConditionalFact]
+ public virtual void Single_string_primary_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleStringKey))!;
+
+ Assert.Equal(
+ [nameof(SingleStringKey.Id)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleStringKey.Id), "$type", nameof(SingleStringKey.Name), nameof(SingleStringKey.P1),
+ nameof(SingleStringKey.P2), nameof(SingleStringKey.P3), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ [ConditionalFact] // Issue #34511
+ public virtual void Single_string_primary_key_with_single_partition_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().HasPartitionKey(e => e.P1);
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleStringKey))!;
+
+ Assert.Equal(
+ [nameof(SingleStringKey.Id), nameof(SingleStringKey.P1)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleStringKey.Id), nameof(SingleStringKey.P1), "$type", nameof(SingleStringKey.Name),
+ nameof(SingleStringKey.P2), nameof(SingleStringKey.P3), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ [ConditionalFact] // Issue #34511
+ public virtual void Single_string_primary_key_with_hierarchical_partition_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().HasPartitionKey(e => new { e.P1, e.P2, e.P3 });
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleStringKey))!;
+
+ Assert.Equal(
+ [nameof(SingleStringKey.Id), nameof(SingleStringKey.P1), nameof(SingleStringKey.P2), nameof(SingleStringKey.P3)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleStringKey.Id), nameof(SingleStringKey.P1), nameof(SingleStringKey.P2), nameof(SingleStringKey.P3),
+ "$type", nameof(SingleStringKey.Name), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ protected class SingleStringKey
+ {
+ public string Id { get; set; } = null!;
+ public string? Name { get; set; }
+ public string P1 { get; set; } = null!;
+ public string P2 { get; set; } = null!;
+ public string P3 { get; set; } = null!;
+ }
+
+ [ConditionalFact] // Issue #34554
+ public virtual void Single_GUID_primary_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleGuidKey))!;
+
+ Assert.Equal(
+ [nameof(SingleGuidKey.Id)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleGuidKey.Id), "$type", nameof(SingleGuidKey.Name), nameof(SingleGuidKey.P1),
+ nameof(SingleGuidKey.P2), nameof(SingleGuidKey.P3), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ [ConditionalFact] // Issue #34554
+ public virtual void Single_GUID_primary_key_with_single_partition_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().HasPartitionKey(e => e.P1);
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleGuidKey))!;
+
+ Assert.Equal(
+ [nameof(SingleGuidKey.Id), nameof(SingleGuidKey.P1)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleGuidKey.Id), nameof(SingleGuidKey.P1), "$type", nameof(SingleGuidKey.Name),
+ nameof(SingleGuidKey.P2), nameof(SingleGuidKey.P3), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ [ConditionalFact] // Issue #34554
+ public virtual void Single_GUID_primary_key_with_hierarchical_partition_key_maps_to_JSON_id()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().HasPartitionKey(e => new { e.P1, e.P2, e.P3 });
+
+ var model = modelBuilder.FinalizeModel();
+
+ var entityType = model.FindEntityType(typeof(SingleGuidKey))!;
+
+ Assert.Equal(
+ [nameof(SingleGuidKey.Id), nameof(SingleGuidKey.P1), nameof(SingleGuidKey.P2), nameof(SingleGuidKey.P3)],
+ entityType.FindPrimaryKey()!.Properties.Select(p => p.Name));
+
+ Assert.Equal(
+ [
+ nameof(SingleGuidKey.Id), nameof(SingleGuidKey.P1), nameof(SingleGuidKey.P2), nameof(SingleGuidKey.P3),
+ "$type", nameof(SingleGuidKey.Name), "__jObject"
+ ],
+ entityType.GetProperties().Select(p => p.Name));
+
+ Assert.Equal(1, entityType.GetKeys().Count());
+ Assert.Null(entityType.FindProperty("__id"));
+ Assert.Equal("id", entityType.FindProperty("Id")!.GetJsonPropertyName());
+ }
+
+ protected class SingleGuidKey
+ {
+ public Guid Id { get; set; }
+ public string? Name { get; set; }
+ public string P1 { get; set; } = null!;
+ public string P2 { get; set; } = null!;
+ public string P3 { get; set; } = null!;
+ }
+
protected override TestModelBuilder CreateModelBuilder(Action? configure = null)
=> new GenericTestModelBuilder(Fixture, configure);
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryDiscriminatorInIdTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryDiscriminatorInIdTest.cs
index 6e098c92d28..56282084c14 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryDiscriminatorInIdTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryDiscriminatorInIdTest.cs
@@ -261,7 +261,7 @@ public override async Task ReadItem_with_hierarchical_partition_key()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("HierarchicalPartitionKeyEntity", "DerivedHierarchicalPartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("HierarchicalPartitionKeyEntity", "DerivedHierarchicalPartitionKeyEntity") AND (c["Id"] = "31887258-bdf9-49b8-89b2-01b6aa741a4a"))
""");
}
@@ -286,7 +286,7 @@ public override async Task ReadItem_with_single_partition_key_constant()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -312,7 +312,7 @@ public override async Task ReadItem_with_single_partition_key_parameter()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -338,7 +338,7 @@ public override async Task ReadItem_with_SingleAsync()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
OFFSET 0 LIMIT 2
""");
}
@@ -365,7 +365,7 @@ public override async Task ReadItem_with_inverse_comparison()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (1 = c["Id"]))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ("b29bced8-e1e5-420e-82d7-1c7a51703d34" = c["Id"]))
""");
}
@@ -391,7 +391,7 @@ public override async Task ReadItem_with_EF_Property()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -404,7 +404,7 @@ public override async Task ReadItem_with_WithPartitionKey()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -428,7 +428,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["Id"] = 2)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["Id"] = "3307a33b-7f28-49ef-9857-48f4e3ebcaed")))
""");
}
@@ -468,7 +468,7 @@ public override async Task ReadItem_is_not_used_without_partition_key()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -479,7 +479,7 @@ public override async Task ReadItem_with_non_existent_id()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 999))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "50b66960-35be-40c5-bc3d-4c9f2799d4d1"))
""");
}
@@ -491,7 +491,7 @@ public override async Task ReadItem_with_AsNoTracking()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -504,7 +504,7 @@ public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -745,7 +745,10 @@ public override async Task ReadItem_with_hierarchical_partition_key_leaf()
{
await base.ReadItem_with_hierarchical_partition_key_leaf();
- AssertSql("""ReadItem(["PK1",1.0,true], DerivedHierarchicalPartitionKeyEntity|11)""");
+ AssertSql(
+ """
+ReadItem(["PK1",1.0,true], DerivedHierarchicalPartitionKeyEntity|316c846c-787f-44b9-aadf-272f1658c5ff)
+""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key_leaf()
@@ -759,7 +762,7 @@ public override async Task ReadItem_with_single_partition_key_constant_leaf()
{
await base.ReadItem_with_single_partition_key_constant_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant_leaf()
@@ -773,7 +776,7 @@ public override async Task ReadItem_with_single_partition_key_parameter_leaf()
{
await base.ReadItem_with_single_partition_key_parameter_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter_leaf()
@@ -787,7 +790,7 @@ public override async Task ReadItem_with_SingleAsync_leaf()
{
await base.ReadItem_with_SingleAsync_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key_leaf()
@@ -801,7 +804,7 @@ public override async Task ReadItem_with_inverse_comparison_leaf()
{
await base.ReadItem_with_inverse_comparison_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key_leaf()
@@ -815,14 +818,14 @@ public override async Task ReadItem_with_EF_Property_leaf()
{
await base.ReadItem_with_EF_Property_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_leaf()
{
await base.ReadItem_with_WithPartitionKey_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key_leaf()
@@ -841,7 +844,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["Id"] = 22)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["Id"] = "11f8d1fd-7472-46f5-9e20-16af42b3b8d1")))
""");
}
@@ -875,7 +878,7 @@ public override async Task ReadItem_is_not_used_without_partition_key_leaf()
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 11))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c"))
""");
}
@@ -883,21 +886,21 @@ public override async Task ReadItem_with_non_existent_id_leaf()
{
await base.ReadItem_with_non_existent_id_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|999)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|b964beda-b4e1-4f5c-a729-0a35dae696fe)""");
}
public override async Task ReadItem_with_AsNoTracking_leaf()
{
await base.ReadItem_with_AsNoTracking_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution_leaf()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_discriminator_mapping()
@@ -909,7 +912,7 @@ public override async Task ReadItem_with_single_explicit_discriminator_mapping()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = "SinglePartitionKeyEntity")))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = "SinglePartitionKeyEntity")))
OFFSET 0 LIMIT 2
""");
}
@@ -923,7 +926,7 @@ public override async Task ReadItem_with_single_explicit_incorrect_discriminator
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
""");
}
@@ -938,7 +941,7 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = @__discriminator_0)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
@@ -947,19 +950,18 @@ public override async Task ReadItem_with_single_explicit_discriminator_mapping_l
{
await base.ReadItem_with_single_explicit_discriminator_mapping_leaf();
- AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], DerivedSinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping_leaf()
{
await base.ReadItem_with_single_explicit_incorrect_discriminator_mapping_leaf();
- // No ReadItem because discriminator value is incorrect
AssertSql(
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = "SinglePartitionKeyEntity")))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = "SinglePartitionKeyEntity")))
""");
}
@@ -974,7 +976,7 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = @__discriminator_0)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryFixtureBase.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryFixtureBase.cs
index b72b194f4bb..03740b7672c 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryFixtureBase.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryFixtureBase.cs
@@ -307,7 +307,7 @@ private static List CreateHierarchicalPartitionK
{
new HierarchicalPartitionKeyEntity
{
- Id = 1,
+ Id = Guid.Parse("31887258-BDF9-49B8-89B2-01B6AA741A4A"),
PartitionKey1 = "PK1",
PartitionKey2 = 1,
PartitionKey3 = true,
@@ -315,7 +315,7 @@ private static List CreateHierarchicalPartitionK
},
new HierarchicalPartitionKeyEntity
{
- Id = 1,
+ Id = Guid.Parse("31887258-BDF9-49B8-89B2-01B6AA741A4A"), // Same Id as previous; different partition.
PartitionKey1 = "PK2",
PartitionKey2 = 2,
PartitionKey3 = false,
@@ -323,7 +323,7 @@ private static List CreateHierarchicalPartitionK
},
new HierarchicalPartitionKeyEntity
{
- Id = 2,
+ Id = Guid.Parse("BBA46A5D-BDB8-40F0-BA80-BA5731147B9A"), // Different Id.
PartitionKey1 = "PK1",
PartitionKey2 = 1,
PartitionKey3 = true,
@@ -331,7 +331,7 @@ private static List CreateHierarchicalPartitionK
},
new HierarchicalPartitionKeyEntity
{
- Id = 2,
+ Id = Guid.Parse("BBA46A5D-BDB8-40F0-BA80-BA5731147B9A"), // Same Id as previous; different partition.
PartitionKey1 = "PK2",
PartitionKey2 = 2,
PartitionKey3 = false,
@@ -344,25 +344,25 @@ private static List CreateSinglePartitionKeyEntities()
{
new SinglePartitionKeyEntity
{
- Id = 1,
+ Id = Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34"),
PartitionKey = "PK1",
Payload = "Payload1"
},
new SinglePartitionKeyEntity
{
- Id = 1,
+ Id = Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34"),
PartitionKey = "PK2",
Payload = "Payload2"
},
new SinglePartitionKeyEntity
{
- Id = 2,
+ Id = Guid.Parse("3307A33B-7F28-49EF-9857-48F4E3EBCAED"),
PartitionKey = "PK1",
Payload = "Payload3"
},
new SinglePartitionKeyEntity
{
- Id = 2,
+ Id = Guid.Parse("3307A33B-7F28-49EF-9857-48F4E3EBCAED"),
PartitionKey = "PK2",
Payload = "Payload4"
}
@@ -485,7 +485,7 @@ private static List CreateSharedContainerEntities2C
public class HierarchicalPartitionKeyEntity
{
- public int Id { get; set; }
+ public Guid Id { get; set; }
public required string PartitionKey1 { get; set; }
public int PartitionKey2 { get; set; }
@@ -496,7 +496,7 @@ public class HierarchicalPartitionKeyEntity
public class SinglePartitionKeyEntity
{
- public int Id { get; set; }
+ public Guid Id { get; set; }
public required string PartitionKey { get; set; }
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceFixtureBase.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceFixtureBase.cs
index 474d4737e25..22a4e04521b 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceFixtureBase.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceFixtureBase.cs
@@ -133,7 +133,7 @@ private static List CreateDerivedHierarch
{
new DerivedHierarchicalPartitionKeyEntity
{
- Id = 11,
+ Id = Guid.Parse("316C846C-787F-44B9-AADF-272F1658C5FF"),
PartitionKey1 = "PK1",
PartitionKey2 = 1,
PartitionKey3 = true,
@@ -142,7 +142,7 @@ private static List CreateDerivedHierarch
},
new DerivedHierarchicalPartitionKeyEntity
{
- Id = 11,
+ Id = Guid.Parse("316C846C-787F-44B9-AADF-272F1658C5FF"), // Same Id as previous; different partition.
PartitionKey1 = "PK2",
PartitionKey2 = 2,
PartitionKey3 = false,
@@ -151,7 +151,7 @@ private static List CreateDerivedHierarch
},
new DerivedHierarchicalPartitionKeyEntity
{
- Id = 22,
+ Id = Guid.Parse("C6E8E6D2-F33E-4695-9FA5-D0E9517EF04E"), // New Id.
PartitionKey1 = "PK1",
PartitionKey2 = 1,
PartitionKey3 = true,
@@ -160,7 +160,7 @@ private static List CreateDerivedHierarch
},
new DerivedHierarchicalPartitionKeyEntity
{
- Id = 22,
+ Id = Guid.Parse("C6E8E6D2-F33E-4695-9FA5-D0E9517EF04E"), // Same Id as previous; different partition.
PartitionKey1 = "PK2",
PartitionKey2 = 2,
PartitionKey3 = false,
@@ -174,28 +174,28 @@ private static List CreateDerivedSinglePartitio
{
new DerivedSinglePartitionKeyEntity
{
- Id = 11,
+ Id = Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C"),
PartitionKey = "PK1",
Payload = "Payload1",
DerivedPayload = "DerivedPayload1"
},
new DerivedSinglePartitionKeyEntity
{
- Id = 11,
+ Id = Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C"),
PartitionKey = "PK2",
Payload = "Payload2",
DerivedPayload = "DerivedPayload2"
},
new DerivedSinglePartitionKeyEntity
{
- Id = 22,
+ Id = Guid.Parse("11F8D1FD-7472-46F5-9E20-16AF42B3B8D1"),
PartitionKey = "PK1",
Payload = "Payload3",
DerivedPayload = "DerivedPayload3"
},
new DerivedSinglePartitionKeyEntity
{
- Id = 22,
+ Id = Guid.Parse("11F8D1FD-7472-46F5-9E20-16AF42B3B8D1"),
PartitionKey = "PK2",
Payload = "Payload4",
DerivedPayload = "DerivedPayload4"
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceTestBase.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceTestBase.cs
index f79173d7eda..b8c5378f9c5 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceTestBase.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryInheritanceTestBase.cs
@@ -154,7 +154,11 @@ public virtual Task ReadItem_with_hierarchical_partition_key_leaf()
return AssertQuery(
async: true,
ss => ss.Set()
- .Where(e => e.Id == 11 && e.PartitionKey1 == "PK1" && e.PartitionKey2 == partitionKey2 && e.PartitionKey3));
+ .Where(
+ e => e.Id == Guid.Parse("316C846C-787F-44B9-AADF-272F1658C5FF")
+ && e.PartitionKey1 == "PK1"
+ && e.PartitionKey2 == partitionKey2
+ && e.PartitionKey3));
}
[ConditionalFact]
@@ -172,7 +176,8 @@ public virtual Task ReadItem_with_only_hierarchical_partition_key_leaf()
public virtual Task ReadItem_with_single_partition_key_constant_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 11 && e.PartitionKey == "PK1"));
+ ss => ss.Set()
+ .Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_only_single_partition_key_constant_leaf()
@@ -187,7 +192,8 @@ public virtual Task ReadItem_with_single_partition_key_parameter_leaf()
return AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 11 && e.PartitionKey == partitionKey));
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -207,7 +213,8 @@ public virtual Task ReadItem_with_SingleAsync_leaf()
return AssertSingle(
async: true,
- ss => ss.Set().Where(e => e.Id == 11 && e.PartitionKey == partitionKey));
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -224,7 +231,8 @@ public virtual Task ReadItem_with_SingleAsync_with_only_partition_key_leaf()
public virtual Task ReadItem_with_inverse_comparison_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => 11 == e.Id && "PK1" == e.PartitionKey));
+ ss => ss.Set()
+ .Where(e => Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") == e.Id && "PK1" == e.PartitionKey));
[ConditionalFact]
public virtual Task ReadItem_with_inverse_comparison_with_only_partition_key_leaf()
@@ -237,15 +245,17 @@ public virtual Task ReadItem_with_EF_Property_leaf()
=> AssertQuery(
async: true,
ss => ss.Set().Where(
- e => EF.Property(e, nameof(DerivedSinglePartitionKeyEntity.Id)) == 11
+ e => EF.Property(e, nameof(DerivedSinglePartitionKeyEntity.Id)) == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")
&& EF.Property(e, nameof(DerivedSinglePartitionKeyEntity.PartitionKey)) == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_WithPartitionKey_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().WithPartitionKey("PK1").Where(e => e.Id == 11),
- ss => ss.Set().Where(e => e.PartitionKey == "PK1").Where(e => e.Id == 11));
+ ss => ss.Set().WithPartitionKey("PK1")
+ .Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")),
+ ss => ss.Set().Where(e => e.PartitionKey == "PK1")
+ .Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")));
[ConditionalFact]
public virtual Task ReadItem_with_WithPartitionKey_with_only_partition_key_leaf()
@@ -261,7 +271,7 @@ public virtual Task Multiple_incompatible_predicate_comparisons_cause_no_ReadIte
return AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 11 && e.Id == 22 && e.PartitionKey == partitionKey),
+ ss => ss.Set().Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.Id == Guid.Parse("11F8D1FD-7472-46F5-9E20-16AF42B3B8D1") && e.PartitionKey == partitionKey),
assertEmpty: true);
}
@@ -287,27 +297,29 @@ public virtual Task ReadItem_with_no_partition_key_leaf()
public virtual Task ReadItem_is_not_used_without_partition_key_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 11));
+ ss => ss.Set().Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")));
[ConditionalFact]
public virtual Task ReadItem_with_non_existent_id_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 999 && e.PartitionKey == "PK1"),
+ ss => ss.Set()
+ .Where(e => e.Id == Guid.Parse("B964BEDA-B4E1-4F5C-A729-0A35DAE696FE") && e.PartitionKey == "PK1"),
assertEmpty: true);
[ConditionalFact]
public virtual Task ReadItem_with_AsNoTracking_leaf()
=> AssertQuery(
async: true,
- ss => ss.Set().AsNoTracking().Where(e => e.Id == 11 && e.PartitionKey == "PK1"));
+ ss => ss.Set().AsNoTracking().Where(
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_AsNoTrackingWithIdentityResolution_leaf()
=> AssertQuery(
async: true,
ss => ss.Set().AsNoTrackingWithIdentityResolution()
- .Where(e => e.Id == 11 && e.PartitionKey == "PK1"));
+ .Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_single_explicit_discriminator_mapping_leaf()
@@ -318,11 +330,11 @@ public virtual Task ReadItem_with_single_explicit_discriminator_mapping_leaf()
async: true,
ss => ss.Set()
.Where(
- e => e.Id == 11
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")
&& EF.Property(e, "$type") == nameof(DerivedSinglePartitionKeyEntity)
&& e.PartitionKey == partitionKey),
ss => ss.Set()
- .Where(e => e.Id == 11 && e.PartitionKey == partitionKey));
+ .Where(e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -334,7 +346,7 @@ public virtual Task ReadItem_with_single_explicit_incorrect_discriminator_mappin
async: true,
ss => ss.Set()
.Where(
- e => e.Id == 11
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")
&& EF.Property(e, "$type") == nameof(SinglePartitionKeyEntity)
&& e.PartitionKey == partitionKey),
ss => ss.Set().Where(e => false),
@@ -350,7 +362,11 @@ public virtual Task ReadItem_with_single_explicit_parameterized_discriminator_ma
return AssertSingle(
async: true,
ss => ss.Set()
- .Where(e => e.Id == 11 && EF.Property(e, "$type") == discriminator && e.PartitionKey == partitionKey),
- ss => ss.Set().Where(e => e.Id == 11 && e.PartitionKey == partitionKey));
+ .Where(
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C")
+ && EF.Property(e, "$type") == discriminator
+ && e.PartitionKey == partitionKey),
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("188D3253-81BE-4A87-B58F-A2BD07E6B98C") && e.PartitionKey == partitionKey));
}
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryNoDiscriminatorInIdTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryNoDiscriminatorInIdTest.cs
index f672784f717..14376779590 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryNoDiscriminatorInIdTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryNoDiscriminatorInIdTest.cs
@@ -236,7 +236,7 @@ public override async Task ReadItem_with_hierarchical_partition_key()
{
await base.ReadItem_with_hierarchical_partition_key();
- AssertSql("""ReadItem(["PK1",1.0,true], 1)""");
+ AssertSql("""ReadItem(["PK1",1.0,true], 31887258-bdf9-49b8-89b2-01b6aa741a4a)""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key()
@@ -250,7 +250,7 @@ public override async Task ReadItem_with_single_partition_key_constant()
{
await base.ReadItem_with_single_partition_key_constant();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant()
@@ -264,7 +264,7 @@ public override async Task ReadItem_with_single_partition_key_parameter()
{
await base.ReadItem_with_single_partition_key_parameter();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter()
@@ -278,7 +278,7 @@ public override async Task ReadItem_with_SingleAsync()
{
await base.ReadItem_with_SingleAsync();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key()
@@ -292,7 +292,7 @@ public override async Task ReadItem_with_inverse_comparison()
{
await base.ReadItem_with_inverse_comparison();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key()
@@ -306,14 +306,14 @@ public override async Task ReadItem_with_EF_Property()
{
await base.ReadItem_with_EF_Property();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey()
{
await base.ReadItem_with_WithPartitionKey();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key()
@@ -332,7 +332,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["Id"] = 2)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["id"] = "3307a33b-7f28-49ef-9857-48f4e3ebcaed")))
""");
}
@@ -366,7 +366,7 @@ public override async Task ReadItem_is_not_used_without_partition_key()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -374,21 +374,21 @@ public override async Task ReadItem_with_non_existent_id()
{
await base.ReadItem_with_non_existent_id();
- AssertSql("""ReadItem(["PK1"], 999)""");
+ AssertSql("""ReadItem(["PK1"], 50b66960-35be-40c5-bc3d-4c9f2799d4d1)""");
}
public override async Task ReadItem_with_AsNoTracking()
{
await base.ReadItem_with_AsNoTracking();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_shared_container()
@@ -618,7 +618,7 @@ public override async Task ReadItem_with_hierarchical_partition_key_leaf()
{
await base.ReadItem_with_hierarchical_partition_key_leaf();
- AssertSql("""ReadItem(["PK1",1.0,true], 11)""");
+ AssertSql("""ReadItem(["PK1",1.0,true], 316c846c-787f-44b9-aadf-272f1658c5ff)""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key_leaf()
@@ -632,7 +632,7 @@ public override async Task ReadItem_with_single_partition_key_constant_leaf()
{
await base.ReadItem_with_single_partition_key_constant_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant_leaf()
@@ -646,7 +646,7 @@ public override async Task ReadItem_with_single_partition_key_parameter_leaf()
{
await base.ReadItem_with_single_partition_key_parameter_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter_leaf()
@@ -660,7 +660,7 @@ public override async Task ReadItem_with_SingleAsync_leaf()
{
await base.ReadItem_with_SingleAsync_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key_leaf()
@@ -674,7 +674,7 @@ public override async Task ReadItem_with_inverse_comparison_leaf()
{
await base.ReadItem_with_inverse_comparison_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key_leaf()
@@ -688,14 +688,14 @@ public override async Task ReadItem_with_EF_Property_leaf()
{
await base.ReadItem_with_EF_Property_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_leaf()
{
await base.ReadItem_with_WithPartitionKey_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key_leaf()
@@ -714,7 +714,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["Id"] = 22)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["id"] = "11f8d1fd-7472-46f5-9e20-16af42b3b8d1")))
""");
}
@@ -748,7 +748,7 @@ public override async Task ReadItem_is_not_used_without_partition_key_leaf()
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 11))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c"))
""");
}
@@ -756,40 +756,39 @@ public override async Task ReadItem_with_non_existent_id_leaf()
{
await base.ReadItem_with_non_existent_id_leaf();
- AssertSql("""ReadItem(["PK1"], 999)""");
+ AssertSql("""ReadItem(["PK1"], b964beda-b4e1-4f5c-a729-0a35dae696fe)""");
}
public override async Task ReadItem_with_AsNoTracking_leaf()
{
await base.ReadItem_with_AsNoTracking_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution_leaf()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_discriminator_mapping()
{
await base.ReadItem_with_single_explicit_discriminator_mapping();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping()
{
await base.ReadItem_with_single_explicit_incorrect_discriminator_mapping();
- // No ReadItem because discriminator value is incorrect
AssertSql(
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
""");
}
@@ -797,14 +796,13 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
{
await base.ReadItem_with_single_explicit_parameterized_discriminator_mapping();
- // No ReadItem because discriminator check is parameterized
AssertSql(
"""
@__discriminator_0='SinglePartitionKeyEntity'
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = @__discriminator_0)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
@@ -813,7 +811,7 @@ public override async Task ReadItem_with_single_explicit_discriminator_mapping_l
{
await base.ReadItem_with_single_explicit_discriminator_mapping_leaf();
- AssertSql("""ReadItem(["PK1"], 11)""");
+ AssertSql("""ReadItem(["PK1"], 188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping_leaf()
@@ -825,7 +823,7 @@ public override async Task ReadItem_with_single_explicit_incorrect_discriminator
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = "SinglePartitionKeyEntity")))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = "SinglePartitionKeyEntity")))
""");
}
@@ -840,7 +838,7 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = @__discriminator_0)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryRootDiscriminatorInIdTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryRootDiscriminatorInIdTest.cs
index 8e0e574a193..f65ca71a1ef 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryRootDiscriminatorInIdTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryRootDiscriminatorInIdTest.cs
@@ -238,7 +238,7 @@ public override async Task ReadItem_with_hierarchical_partition_key()
{
await base.ReadItem_with_hierarchical_partition_key();
- AssertSql("""ReadItem(["PK1",1.0,true], HierarchicalPartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1",1.0,true], HierarchicalPartitionKeyEntity|31887258-bdf9-49b8-89b2-01b6aa741a4a)""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key()
@@ -252,7 +252,7 @@ public override async Task ReadItem_with_single_partition_key_constant()
{
await base.ReadItem_with_single_partition_key_constant();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant()
@@ -266,7 +266,7 @@ public override async Task ReadItem_with_single_partition_key_parameter()
{
await base.ReadItem_with_single_partition_key_parameter();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter()
@@ -280,7 +280,7 @@ public override async Task ReadItem_with_SingleAsync()
{
await base.ReadItem_with_SingleAsync();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key()
@@ -294,7 +294,7 @@ public override async Task ReadItem_with_inverse_comparison()
{
await base.ReadItem_with_inverse_comparison();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key()
@@ -308,14 +308,14 @@ public override async Task ReadItem_with_EF_Property()
{
await base.ReadItem_with_EF_Property();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey()
{
await base.ReadItem_with_WithPartitionKey();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key()
@@ -334,7 +334,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["Id"] = 2)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["Id"] = "3307a33b-7f28-49ef-9857-48f4e3ebcaed")))
""");
}
@@ -368,7 +368,7 @@ public override async Task ReadItem_is_not_used_without_partition_key()
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 1))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34"))
""");
}
@@ -376,21 +376,21 @@ public override async Task ReadItem_with_non_existent_id()
{
await base.ReadItem_with_non_existent_id();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|999)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|50b66960-35be-40c5-bc3d-4c9f2799d4d1)""");
}
public override async Task ReadItem_with_AsNoTracking()
{
await base.ReadItem_with_AsNoTracking();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_shared_container()
@@ -622,7 +622,7 @@ public override async Task ReadItem_with_hierarchical_partition_key_leaf()
{
await base.ReadItem_with_hierarchical_partition_key_leaf();
- AssertSql("""ReadItem(["PK1",1.0,true], HierarchicalPartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1",1.0,true], HierarchicalPartitionKeyEntity|316c846c-787f-44b9-aadf-272f1658c5ff)""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key_leaf()
@@ -636,7 +636,7 @@ public override async Task ReadItem_with_single_partition_key_constant_leaf()
{
await base.ReadItem_with_single_partition_key_constant_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant_leaf()
@@ -650,7 +650,7 @@ public override async Task ReadItem_with_single_partition_key_parameter_leaf()
{
await base.ReadItem_with_single_partition_key_parameter_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter_leaf()
@@ -664,7 +664,7 @@ public override async Task ReadItem_with_SingleAsync_leaf()
{
await base.ReadItem_with_SingleAsync_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key_leaf()
@@ -678,7 +678,7 @@ public override async Task ReadItem_with_inverse_comparison_leaf()
{
await base.ReadItem_with_inverse_comparison_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key_leaf()
@@ -692,14 +692,14 @@ public override async Task ReadItem_with_EF_Property_leaf()
{
await base.ReadItem_with_EF_Property_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_leaf()
{
await base.ReadItem_with_WithPartitionKey_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key_leaf()
@@ -718,7 +718,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["Id"] = 22)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["Id"] = "11f8d1fd-7472-46f5-9e20-16af42b3b8d1")))
""");
}
@@ -752,7 +752,7 @@ public override async Task ReadItem_is_not_used_without_partition_key_leaf()
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["Id"] = 11))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND (c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c"))
""");
}
@@ -760,28 +760,28 @@ public override async Task ReadItem_with_non_existent_id_leaf()
{
await base.ReadItem_with_non_existent_id_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|999)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b964beda-b4e1-4f5c-a729-0a35dae696fe)""");
}
public override async Task ReadItem_with_AsNoTracking_leaf()
{
await base.ReadItem_with_AsNoTracking_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution_leaf()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_discriminator_mapping()
{
await base.ReadItem_with_single_explicit_discriminator_mapping();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|1)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping()
@@ -793,7 +793,7 @@ public override async Task ReadItem_with_single_explicit_incorrect_discriminator
"""
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = "DerivedSinglePartitionKeyEntity")))
""");
}
@@ -808,7 +808,7 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
SELECT VALUE c
FROM root c
-WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 1) AND (c["$type"] = @__discriminator_0)))
+WHERE (c["$type"] IN ("SinglePartitionKeyEntity", "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
@@ -817,7 +817,7 @@ public override async Task ReadItem_with_single_explicit_discriminator_mapping_l
{
await base.ReadItem_with_single_explicit_discriminator_mapping_leaf();
- AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|11)""");
+ AssertSql("""ReadItem(["PK1"], SinglePartitionKeyEntity|188d3253-81be-4a87-b58f-a2bd07e6b98c)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping_leaf()
@@ -829,7 +829,7 @@ public override async Task ReadItem_with_single_explicit_incorrect_discriminator
"""
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = "SinglePartitionKeyEntity")))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = "SinglePartitionKeyEntity")))
""");
}
@@ -844,7 +844,7 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
SELECT VALUE c
FROM root c
-WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = 11) AND (c["$type"] = @__discriminator_0)))
+WHERE ((c["$type"] = "DerivedSinglePartitionKeyEntity") AND ((c["Id"] = "188d3253-81be-4a87-b58f-a2bd07e6b98c") AND (c["$type"] = @__discriminator_0)))
OFFSET 0 LIMIT 2
""");
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTest.cs
index b31eb485e6e..a5bf0e45536 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTest.cs
@@ -234,7 +234,7 @@ public override async Task ReadItem_with_hierarchical_partition_key()
{
await base.ReadItem_with_hierarchical_partition_key();
- AssertSql("""ReadItem(["PK1",1.0,true], 1)""");
+ AssertSql("""ReadItem(["PK1",1.0,true], 31887258-bdf9-49b8-89b2-01b6aa741a4a)""");
}
public override async Task ReadItem_with_only_hierarchical_partition_key()
@@ -248,7 +248,7 @@ public override async Task ReadItem_with_single_partition_key_constant()
{
await base.ReadItem_with_single_partition_key_constant();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_constant()
@@ -262,7 +262,7 @@ public override async Task ReadItem_with_single_partition_key_parameter()
{
await base.ReadItem_with_single_partition_key_parameter();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_only_single_partition_key_parameter()
@@ -276,7 +276,7 @@ public override async Task ReadItem_with_SingleAsync()
{
await base.ReadItem_with_SingleAsync();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_SingleAsync_with_only_partition_key()
@@ -290,7 +290,7 @@ public override async Task ReadItem_with_inverse_comparison()
{
await base.ReadItem_with_inverse_comparison();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_inverse_comparison_with_only_partition_key()
@@ -304,14 +304,14 @@ public override async Task ReadItem_with_EF_Property()
{
await base.ReadItem_with_EF_Property();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey()
{
await base.ReadItem_with_WithPartitionKey();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_WithPartitionKey_with_only_partition_key()
@@ -330,7 +330,7 @@ public override async Task Multiple_incompatible_predicate_comparisons_cause_no_
"""
SELECT VALUE c
FROM root c
-WHERE ((c["Id"] = 1) AND (c["Id"] = 2))
+WHERE ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["id"] = "3307a33b-7f28-49ef-9857-48f4e3ebcaed"))
""");
}
@@ -365,7 +365,7 @@ public override async Task ReadItem_is_not_used_without_partition_key()
"""
SELECT VALUE c
FROM root c
-WHERE (c["Id"] = 1)
+WHERE (c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34")
""");
}
@@ -373,21 +373,21 @@ public override async Task ReadItem_with_non_existent_id()
{
await base.ReadItem_with_non_existent_id();
- AssertSql("""ReadItem(["PK1"], 999)""");
+ AssertSql("""ReadItem(["PK1"], 50b66960-35be-40c5-bc3d-4c9f2799d4d1)""");
}
public override async Task ReadItem_with_AsNoTracking()
{
await base.ReadItem_with_AsNoTracking();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_AsNoTrackingWithIdentityResolution()
{
await base.ReadItem_with_AsNoTrackingWithIdentityResolution();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_shared_container()
@@ -415,7 +415,7 @@ public override async Task ReadItem_with_single_explicit_discriminator_mapping()
{
await base.ReadItem_with_single_explicit_discriminator_mapping();
- AssertSql("""ReadItem(["PK1"], 1)""");
+ AssertSql("""ReadItem(["PK1"], b29bced8-e1e5-420e-82d7-1c7a51703d34)""");
}
public override async Task ReadItem_with_single_explicit_incorrect_discriminator_mapping()
@@ -427,7 +427,7 @@ public override async Task ReadItem_with_single_explicit_incorrect_discriminator
"""
SELECT VALUE c
FROM root c
-WHERE ((c["Id"] = 1) AND (c["$type"] = "DerivedSinglePartitionKeyEntity"))
+WHERE ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = "DerivedSinglePartitionKeyEntity"))
""");
}
@@ -435,14 +435,13 @@ public override async Task ReadItem_with_single_explicit_parameterized_discrimin
{
await base.ReadItem_with_single_explicit_parameterized_discriminator_mapping();
- // No ReadItem because discriminator check is parameterized
AssertSql(
"""
@__discriminator_0='SinglePartitionKeyEntity'
SELECT VALUE c
FROM root c
-WHERE ((c["Id"] = 1) AND (c["$type"] = @__discriminator_0))
+WHERE ((c["id"] = "b29bced8-e1e5-420e-82d7-1c7a51703d34") AND (c["$type"] = @__discriminator_0))
OFFSET 0 LIMIT 2
""");
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTestBase.cs b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTestBase.cs
index 5a0bedaf279..e2c1bc33712 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTestBase.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/ReadItemPartitionKeyQueryTestBase.cs
@@ -161,7 +161,11 @@ public virtual Task ReadItem_with_hierarchical_partition_key()
return AssertQuery(
async: true,
ss => ss.Set()
- .Where(e => e.Id == 1 && e.PartitionKey1 == "PK1" && e.PartitionKey2 == partitionKey2 && e.PartitionKey3));
+ .Where(
+ e => e.Id == Guid.Parse("31887258-BDF9-49B8-89B2-01B6AA741A4A")
+ && e.PartitionKey1 == "PK1"
+ && e.PartitionKey2 == partitionKey2
+ && e.PartitionKey3));
}
[ConditionalFact]
@@ -179,7 +183,8 @@ public virtual Task ReadItem_with_only_hierarchical_partition_key()
public virtual Task ReadItem_with_single_partition_key_constant()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 1 && e.PartitionKey == "PK1"));
+ ss => ss.Set()
+ .Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_only_single_partition_key_constant()
@@ -194,7 +199,8 @@ public virtual Task ReadItem_with_single_partition_key_parameter()
return AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 1 && e.PartitionKey == partitionKey));
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -214,7 +220,8 @@ public virtual Task ReadItem_with_SingleAsync()
return AssertSingle(
async: true,
- ss => ss.Set().Where(e => e.Id == 1 && e.PartitionKey == partitionKey));
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -231,7 +238,8 @@ public virtual Task ReadItem_with_SingleAsync_with_only_partition_key()
public virtual Task ReadItem_with_inverse_comparison()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => 1 == e.Id && "PK1" == e.PartitionKey));
+ ss => ss.Set()
+ .Where(e => Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") == e.Id && "PK1" == e.PartitionKey));
[ConditionalFact]
public virtual Task ReadItem_with_inverse_comparison_with_only_partition_key()
@@ -244,15 +252,17 @@ public virtual Task ReadItem_with_EF_Property()
=> AssertQuery(
async: true,
ss => ss.Set().Where(
- e => EF.Property(e, nameof(SinglePartitionKeyEntity.Id)) == 1
+ e => EF.Property(e, nameof(SinglePartitionKeyEntity.Id)) == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")
&& EF.Property(e, nameof(SinglePartitionKeyEntity.PartitionKey)) == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_WithPartitionKey()
=> AssertQuery(
async: true,
- ss => ss.Set().WithPartitionKey("PK1").Where(e => e.Id == 1),
- ss => ss.Set().Where(e => e.PartitionKey == "PK1").Where(e => e.Id == 1));
+ ss => ss.Set().WithPartitionKey("PK1")
+ .Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")),
+ ss => ss.Set().Where(e => e.PartitionKey == "PK1")
+ .Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")));
[ConditionalFact]
public virtual Task ReadItem_with_WithPartitionKey_with_only_partition_key()
@@ -268,7 +278,10 @@ public virtual Task Multiple_incompatible_predicate_comparisons_cause_no_ReadIte
return AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 1 && e.Id == 2 && e.PartitionKey == partitionKey),
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")
+ && e.Id == Guid.Parse("3307A33B-7F28-49EF-9857-48F4E3EBCAED")
+ && e.PartitionKey == partitionKey),
assertEmpty: true);
}
@@ -294,26 +307,26 @@ public virtual Task ReadItem_with_no_partition_key()
public virtual Task ReadItem_is_not_used_without_partition_key()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 1));
+ ss => ss.Set().Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")));
[ConditionalFact]
public virtual Task ReadItem_with_non_existent_id()
=> AssertQuery(
async: true,
- ss => ss.Set().Where(e => e.Id == 999 && e.PartitionKey == "PK1"),
+ ss => ss.Set().Where(e => e.Id == Guid.Parse("50B66960-35BE-40C5-BC3D-4C9F2799D4D1") && e.PartitionKey == "PK1"),
assertEmpty: true);
[ConditionalFact]
public virtual Task ReadItem_with_AsNoTracking()
=> AssertQuery(
async: true,
- ss => ss.Set().AsNoTracking().Where(e => e.Id == 1 && e.PartitionKey == "PK1"));
+ ss => ss.Set().AsNoTracking().Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_AsNoTrackingWithIdentityResolution()
=> AssertQuery(
async: true,
- ss => ss.Set().AsNoTrackingWithIdentityResolution().Where(e => e.Id == 1 && e.PartitionKey == "PK1"));
+ ss => ss.Set().AsNoTrackingWithIdentityResolution().Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == "PK1"));
[ConditionalFact]
public virtual Task ReadItem_with_shared_container()
@@ -342,11 +355,11 @@ public virtual Task ReadItem_with_single_explicit_discriminator_mapping()
async: true,
ss => ss.Set()
.Where(
- e => e.Id == 1
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")
&& EF.Property(e, "$type") == nameof(SinglePartitionKeyEntity)
&& e.PartitionKey == partitionKey),
ss => ss.Set()
- .Where(e => e.Id == 1 && e.PartitionKey == partitionKey));
+ .Where(e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == partitionKey));
}
[ConditionalFact]
@@ -358,7 +371,7 @@ public virtual Task ReadItem_with_single_explicit_incorrect_discriminator_mappin
async: true,
ss => ss.Set()
.Where(
- e => e.Id == 1
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")
&& EF.Property(e, "$type") == nameof(DerivedSinglePartitionKeyEntity)
&& e.PartitionKey == partitionKey),
ss => ss.Set().Where(e => false),
@@ -374,8 +387,12 @@ public virtual Task ReadItem_with_single_explicit_parameterized_discriminator_ma
return AssertSingle(
async: true,
ss => ss.Set()
- .Where(e => e.Id == 1 && EF.Property(e, "$type") == discriminator && e.PartitionKey == partitionKey),
- ss => ss.Set().Where(e => e.Id == 1 && e.PartitionKey == partitionKey));
+ .Where(
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34")
+ && EF.Property(e, "$type") == discriminator
+ && e.PartitionKey == partitionKey),
+ ss => ss.Set().Where(
+ e => e.Id == Guid.Parse("B29BCED8-E1E5-420E-82D7-1C7A51703D34") && e.PartitionKey == partitionKey));
}
protected void AssertSql(params string[] expected)
diff --git a/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs b/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs
index c2c58289d54..ee13f8d8c34 100644
--- a/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs
+++ b/test/EFCore.Design.Tests/Design/DesignTimeServicesTest.cs
@@ -192,6 +192,8 @@ public bool IsValidId(string value)
public class ExtensionHistoryRepository : IHistoryRepository
{
+ public virtual LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
+
public void Create()
=> throw new NotImplementedException();
@@ -222,10 +224,10 @@ public string GetCreateIfNotExistsScript()
public string GetCreateScript()
=> throw new NotImplementedException();
- public IDisposable GetDatabaseLock()
+ public IMigrationsDatabaseLock AcquireDatabaseLock()
=> throw new NotImplementedException();
- public Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public string GetDeleteScript(string migrationId)
@@ -264,6 +266,8 @@ public bool IsValidId(string value)
public class ContextHistoryRepository : IHistoryRepository
{
+ public virtual LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
+
public bool Exists()
=> throw new NotImplementedException();
@@ -294,10 +298,10 @@ public void Create()
public Task CreateAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
- public IDisposable GetDatabaseLock()
+ public IMigrationsDatabaseLock AcquireDatabaseLock()
=> throw new NotImplementedException();
- public Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
public string GetDeleteScript(string migrationId)
diff --git a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj
index e7ff39b5371..36abc41e4cf 100644
--- a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj
+++ b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj
@@ -56,7 +56,7 @@
-
+
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs
index a3ac3eff14b..d9216f9dfe5 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs
@@ -5944,6 +5944,72 @@ public virtual void SQLServer_property_legacy_identity_seed_int_annotation()
#endregion
+ #region Primitive collection
+
+ [ConditionalFact]
+ public virtual void PrimitiveCollection_is_stored_in_snapshot()
+ => Test(
+ builder =>
+ {
+ builder.Entity()
+ .PrimitiveCollection>("List")
+ .IsSparse()
+ .IsFixedLength()
+ .HasMaxLength(100)
+ .IsUnicode()
+ .UseCollation("ListCollation")
+ .HasSentinel([])
+ .HasColumnName("ListColumn")
+ .HasColumnType("nvarchar")
+ .HasColumnOrder(1)
+ .HasComment("ListComment")
+ .HasComputedColumnSql("ListSql")
+ .HasJsonPropertyName("ListJson")
+ .ElementType(b => b.HasConversion())
+ .ValueGeneratedOnUpdateSometimes()
+ .HasAnnotation("AnnotationName", "AnnotationValue");
+
+ builder.Ignore();
+ },
+ AddBoilerPlate(
+ GetHeading()
+ + """
+ modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.PrimitiveCollection("List")
+ .ValueGeneratedOnUpdateSometimes()
+ .HasMaxLength(100)
+ .IsUnicode(true)
+ .HasColumnType("nvarchar")
+ .HasColumnName("ListColumn")
+ .HasColumnOrder(1)
+ .HasComputedColumnSql("ListSql")
+ .IsFixedLength()
+ .HasComment("ListComment")
+ .UseCollation("ListCollation")
+ .HasAnnotation("AnnotationName", "AnnotationValue")
+ .HasAnnotation("Relational:JsonPropertyName", "ListJson");
+
+ SqlServerPrimitiveCollectionBuilderExtensions.IsSparse(b.PrimitiveCollection("List"));
+
+ b.HasKey("Id");
+
+ b.ToTable("EntityWithOneProperty", "DefaultSchema");
+ });
+"""),
+ o =>
+ {
+ var property = o.GetEntityTypes().First().FindProperty("List");
+ Assert.Equal("AnnotationValue", property["AnnotationName"]);
+ });
+ #endregion
+
#region Complex types
[ConditionalFact]
@@ -5958,8 +6024,13 @@ public virtual void Complex_properties_are_stored_in_snapshot()
eo => eo.EntityWithTwoProperties, eb =>
{
eb.IsRequired();
- eb.Property(e => e.AlternateId).HasColumnOrder(1);
- eb.ComplexProperty(e => e.EntityWithStringKey).IsRequired();
+ eb.Property(e => e.AlternateId).HasColumnOrder(1).IsSparse();
+ eb.PrimitiveCollection>("List")
+ .HasColumnType("nvarchar(max)")
+ .IsSparse();
+ eb.ComplexProperty(e => e.EntityWithStringKey)
+ .IsRequired()
+ .Ignore(e => e.Properties);
eb.HasPropertyAnnotation("PropertyAnnotation", 1);
eb.HasTypeAnnotation("TypeAnnotation", 2);
});
@@ -5984,9 +6055,16 @@ public virtual void Complex_properties_are_stored_in_snapshot()
.HasColumnType("int")
.HasColumnOrder(1);
+ SqlServerComplexTypePropertyBuilderExtensions.IsSparse(b1.Property("AlternateId"));
+
b1.Property("Id")
.HasColumnType("int");
+ b1.PrimitiveCollection("List")
+ .HasColumnType("nvarchar(max)");
+
+ SqlServerComplexTypePrimitiveCollectionBuilderExtensions.IsSparse(b1.PrimitiveCollection("List"));
+
b1.ComplexProperty>("EntityWithStringKey", "Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+EntityWithOneProperty.EntityWithTwoProperties#EntityWithTwoProperties.EntityWithStringKey#EntityWithStringKey", b2 =>
{
b2.IsRequired();
@@ -6005,7 +6083,7 @@ public virtual void Complex_properties_are_stored_in_snapshot()
b.ToTable("EntityWithOneProperty", "DefaultSchema");
});
""", usingCollections: true),
- o =>
+ (_, o) =>
{
var entityWithOneProperty = o.FindEntityType(typeof(EntityWithOneProperty));
Assert.Equal(nameof(EntityWithOneProperty), entityWithOneProperty.GetTableName());
@@ -6037,7 +6115,8 @@ public virtual void Complex_properties_are_stored_in_snapshot()
Assert.Equal(nameof(EntityWithOneProperty), nestedComplexType.GetTableName());
var nestedIdProperty = nestedComplexType.FindProperty(nameof(EntityWithStringKey.Id));
Assert.True(nestedIdProperty.IsNullable);
- });
+ },
+ validate: true);
#endregion
@@ -7981,7 +8060,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
- b.Property("BoolCollection")
+ b.PrimitiveCollection("BoolCollection")
.HasColumnType("nvarchar(max)");
b.Property("Boolean")
@@ -7993,7 +8072,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Bytes")
.HasColumnType("varbinary(max)");
- b.Property("BytesCollection")
+ b.PrimitiveCollection("BytesCollection")
.HasColumnType("nvarchar(max)");
b.Property("Character")
@@ -8003,7 +8082,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("DateTime")
.HasColumnType("datetime2");
- b.Property("DateTimeCollection")
+ b.PrimitiveCollection("DateTimeCollection")
.HasColumnType("nvarchar(max)");
b.Property("DateTimeOffset")
@@ -8015,7 +8094,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Double")
.HasColumnType("float");
- b.Property("DoubleCollection")
+ b.PrimitiveCollection("DoubleCollection")
.HasColumnType("nvarchar(max)");
b.Property("Enum16")
@@ -8048,7 +8127,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Int32")
.HasColumnType("int");
- b.Property("Int32Collection")
+ b.PrimitiveCollection("Int32Collection")
.HasColumnType("nvarchar(max)");
b.Property("Int64")
@@ -8108,7 +8187,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("String")
.HasColumnType("nvarchar(max)");
- b.Property("StringCollection")
+ b.PrimitiveCollection("StringCollection")
.HasColumnType("nvarchar(max)");
b.Property("TimeSpan")
@@ -8403,7 +8482,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
protected void Test(Action buildModel, string expectedCode, Action assert)
=> Test(buildModel, expectedCode, (m, _) => assert(m));
- protected void Test(Action buildModel, string expectedCode, Action assert)
+ protected void Test(Action buildModel, string expectedCode, Action assert, bool validate = false)
{
var modelBuilder = CreateConventionalModelBuilder();
modelBuilder.HasDefaultSchema("DefaultSchema");
@@ -8411,7 +8490,7 @@ protected void Test(Action buildModel, string expectedCode, Action
modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
buildModel(modelBuilder);
- var model = modelBuilder.FinalizeModel(designTime: true, skipValidation: true);
+ var model = modelBuilder.FinalizeModel(designTime: true, skipValidation: !validate);
Test(model, expectedCode, assert);
}
diff --git a/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs b/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs
index 94fa478789e..45f138e904b 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/MigrationScaffolderTest.cs
@@ -125,7 +125,8 @@ var migrationAssembly
services.GetRequiredService(),
services.GetRequiredService(),
services.GetRequiredService(),
- services.GetRequiredService())));
+ services.GetRequiredService(),
+ services.GetRequiredService())));
}
// ReSharper disable once UnusedTypeParameter
@@ -143,6 +144,8 @@ protected override void BuildModel(ModelBuilder modelBuilder)
private class MockHistoryRepository : IHistoryRepository
{
+ public virtual LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
+
public string GetBeginIfExistsScript(string migrationId)
=> null;
@@ -182,10 +185,10 @@ public void Create()
public Task CreateAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
- public IDisposable GetDatabaseLock()
+ public IMigrationsDatabaseLock AcquireDatabaseLock()
=> throw new NotImplementedException();
- public Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
}
diff --git a/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj b/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj
index e3e6935f96b..94f59331dcb 100644
--- a/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj
+++ b/test/EFCore.Relational.Specification.Tests/EFCore.Relational.Specification.Tests.csproj
@@ -50,10 +50,8 @@
-
-
-
-
+
+
diff --git a/test/EFCore.Relational.Tests/Extensions/RelationalDatabaseFacadeExtensionsTest.cs b/test/EFCore.Relational.Tests/Extensions/RelationalDatabaseFacadeExtensionsTest.cs
index 3e56284521a..f2d2a935de3 100644
--- a/test/EFCore.Relational.Tests/Extensions/RelationalDatabaseFacadeExtensionsTest.cs
+++ b/test/EFCore.Relational.Tests/Extensions/RelationalDatabaseFacadeExtensionsTest.cs
@@ -321,6 +321,8 @@ protected override void BuildModel(ModelBuilder modelBuilder)
private class FakeHistoryRepository : IHistoryRepository
{
+ public virtual LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Explicit;
+
public List AppliedMigrations { get; set; }
public IReadOnlyList GetAppliedMigrations()
@@ -362,10 +364,10 @@ public void Create()
public Task CreateAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
- public IDisposable GetDatabaseLock()
+ public IMigrationsDatabaseLock AcquireDatabaseLock()
=> throw new NotImplementedException();
- public Task GetDatabaseLockAsync(CancellationToken cancellationToken = default)
+ public Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default)
=> throw new NotImplementedException();
}
diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
index 91747976f02..3c00a36a40b 100644
--- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
+++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs
@@ -8646,7 +8646,7 @@ public void Split_out_subtype_with_seed_data()
x.Property("Name");
x.Property("Discriminator");
- x.HasDiscriminator("Discriminator")
+ x.HasDiscriminator()
.HasValue(1)
.HasValue(2);
diff --git a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.OwnedTypes.cs b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.OwnedTypes.cs
index 811c6fae39e..c1a251a6471 100644
--- a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.OwnedTypes.cs
+++ b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.OwnedTypes.cs
@@ -1125,11 +1125,12 @@ public virtual void Can_map_derived_of_owned_type_first()
}
[ConditionalFact]
- public virtual void Can_configure_relationship_with_PK_ValueConverter()
+ public virtual void Can_configure_relationship_with_PK_ValueConverter_shadow_FK()
{
var modelBuilder = CreateModelBuilder();
- modelBuilder.Entity().Property(x => x.Id)
+ modelBuilder.Entity()
+ .Property(x => x.Id)
.HasConversion(x => x.Id, x => new CustomId { Id = x });
modelBuilder.Entity()
@@ -1138,8 +1139,7 @@ public virtual void Can_configure_relationship_with_PK_ValueConverter()
modelBuilder.Entity()
.OwnsOne(q => q.Value)
- .Property(x => x.CategoryId)
- .HasConversion(x => x.Id, x => new CustomId { Id = x });
+ .Property(x => x.CategoryId);
var model = modelBuilder.FinalizeModel();
@@ -1155,14 +1155,45 @@ public virtual void Can_configure_relationship_with_PK_ValueConverter()
var category = model.FindEntityType(typeof(ValueCategory));
Assert.Null(category.FindProperty("TempId"));
- var barNavigation = owned.GetDeclaredNavigations().Single(n => !n.ForeignKey.IsOwnership);
- Assert.Same(category, barNavigation.TargetEntityType);
- var fkProperty = barNavigation.ForeignKey.Properties.Single();
+ var categoryNavigation = owned.GetDeclaredNavigations().Single(n => !n.ForeignKey.IsOwnership);
+ Assert.Same(category, categoryNavigation.TargetEntityType);
+ var fkProperty = categoryNavigation.ForeignKey.Properties.Single();
Assert.Equal("CategoryId", fkProperty.Name);
Assert.Equal(3, model.GetEntityTypes().Count());
}
+ [ConditionalFact]
+ public virtual void Can_configure_relationship_with_PK_ValueConverter()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity(eb =>
+ {
+ eb.Property(x => x.Id)
+ .HasConversion(x => x.Id, x => new CustomId { Id = x });
+ eb.OwnsOne(q => q.Value)
+ .WithOwner()
+ .HasForeignKey(q => q.CategoryId);
+ });
+
+ modelBuilder.Ignore();
+
+ var model = modelBuilder.FinalizeModel();
+
+ var result = model.FindEntityType(typeof(QueryResult));
+ Assert.Null(result.FindProperty("TempId"));
+
+ var owned = result.GetDeclaredNavigations().Single().TargetEntityType;
+ Assert.Null(owned.FindProperty("TempId"));
+
+ var ownedPkProperty = owned.FindPrimaryKey().Properties.Single();
+ Assert.NotNull(ownedPkProperty.GetValueConverter());
+
+ Assert.DoesNotContain(owned.GetDeclaredNavigations(), n => !n.ForeignKey.IsOwnership);
+ Assert.Equal(2, model.GetEntityTypes().Count());
+ }
+
[ConditionalFact]
public virtual void Throws_on_FK_matching_two_relationships()
{
diff --git a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.TestModel.cs b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.TestModel.cs
index 483deaa4c5e..7bfb5484941 100644
--- a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.TestModel.cs
+++ b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.TestModel.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
@@ -784,9 +785,12 @@ protected class Value
public ValueCategory? Category { get; set; }
}
- protected class CustomId
+ protected class CustomId : IEnumerable
{
public int Id { get; set; }
+
+ public IEnumerator GetEnumerator() => throw new NotImplementedException();
+ IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
}
protected class ValueCategory
diff --git a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.cs b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.cs
index 75d69f0d5fd..1664aa0fb16 100644
--- a/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.cs
+++ b/test/EFCore.Specification.Tests/ModelBuilding/ModelBuilderTest.cs
@@ -61,7 +61,7 @@ protected TestModelBuilder(ModelBuilderFixtureBase fixture, Action l == DbLoggerCategory.Model.Validation.Name);
ValidationLogger = new DiagnosticsLogger(
ValidationLoggerFactory,
@@ -86,6 +86,9 @@ protected TestModelBuilder(ModelBuilderFixtureBase fixture, Action builder.EnableSensitiveDataLogging(false);
+
public virtual IMutableModel Model
=> ModelBuilder.Model;
diff --git a/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsInfrastructureSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsInfrastructureSqlServerTest.cs
index 8aeb88a4e31..3d1c63e96ef 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsInfrastructureSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Migrations/MigrationsInfrastructureSqlServerTest.cs
@@ -1017,13 +1017,16 @@ SELECT 1
EXEC @result = sp_getapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session', @LockMode = 'Exclusive';
SELECT @result
-SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
+IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
+BEGIN
+ CREATE TABLE [__EFMigrationsHistory] (
+ [MigrationId] nvarchar(150) NOT NULL,
+ [ProductVersion] nvarchar(32) NOT NULL,
+ CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
+ );
+END;
-CREATE TABLE [__EFMigrationsHistory] (
- [MigrationId] nvarchar(150) NOT NULL,
- [ProductVersion] nvarchar(32) NOT NULL,
- CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
-);
+SELECT 1
SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
@@ -1047,6 +1050,22 @@ CONSTRAINT [PK_Blogs] PRIMARY KEY ([Id])
THROW 65536, 'Test', 0;
END
+DECLARE @result int;
+EXEC @result = sp_releaseapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session';
+SELECT @result
+
+DECLARE @result int;
+EXEC @result = sp_getapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session', @LockMode = 'Exclusive';
+SELECT @result
+
+SELECT 1
+
+SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
+
+SELECT [MigrationId], [ProductVersion]
+FROM [__EFMigrationsHistory]
+ORDER BY [MigrationId];
+
IF OBJECT_ID(N'Blogs', N'U') IS NULL
BEGIN
CREATE TABLE [Blogs] (
@@ -1110,13 +1129,16 @@ SELECT 1
EXEC @result = sp_getapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session', @LockMode = 'Exclusive';
SELECT @result
-SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
+IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
+BEGIN
+ CREATE TABLE [__EFMigrationsHistory] (
+ [MigrationId] nvarchar(150) NOT NULL,
+ [ProductVersion] nvarchar(32) NOT NULL,
+ CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
+ );
+END;
-CREATE TABLE [__EFMigrationsHistory] (
- [MigrationId] nvarchar(150) NOT NULL,
- [ProductVersion] nvarchar(32) NOT NULL,
- CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
-);
+SELECT 1
SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
@@ -1140,6 +1162,22 @@ CONSTRAINT [PK_Blogs] PRIMARY KEY ([Id])
THROW 65536, 'Test', 0;
END
+DECLARE @result int;
+EXEC @result = sp_releaseapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session';
+SELECT @result
+
+DECLARE @result int;
+EXEC @result = sp_getapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session', @LockMode = 'Exclusive';
+SELECT @result
+
+SELECT 1
+
+SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
+
+SELECT [MigrationId], [ProductVersion]
+FROM [__EFMigrationsHistory]
+ORDER BY [MigrationId];
+
IF OBJECT_ID(N'Blogs', N'U') IS NULL
BEGIN
CREATE TABLE [Blogs] (
@@ -2078,7 +2116,8 @@ IF EXISTS(select * from sys.databases where name='TransactionSuppressed')
public override MigrationsContext CreateContext()
{
var options = AddOptions(TestStore.AddProviderOptions(new DbContextOptionsBuilder()))
- .UseSqlServer(TestStore.ConnectionString, b => b.ApplyConfiguration())
+ .UseSqlServer(TestStore.ConnectionString, b => b
+ .ApplyConfiguration())
.UseInternalServiceProvider(ServiceProvider)
.Options;
return new MigrationsContext(options);
diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerRetryingExecutionStrategy.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerRetryingExecutionStrategy.cs
index ec517ff802f..ac0eea2e54c 100644
--- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerRetryingExecutionStrategy.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/TestSqlServerRetryingExecutionStrategy.cs
@@ -43,6 +43,13 @@ public TestSqlServerRetryingExecutionStrategy(ExecutionStrategyDependencies depe
{
}
+ public TestSqlServerRetryingExecutionStrategy(
+ ExecutionStrategyDependencies dependencies,
+ IEnumerable errorNumbersToAdd)
+ : base(dependencies, DefaultMaxRetryCount, DefaultMaxDelay, _additionalErrorNumbers.Concat(errorNumbersToAdd))
+ {
+ }
+
protected override bool ShouldRetryOn(Exception exception)
{
if (base.ShouldRetryOn(exception))
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
index 19e54bf361c..e6ca88c0332 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
@@ -47,7 +47,6 @@ public virtual void Detects_well_known_concrete_collections_mapped_as_owned_enti
var modelBuilder = CreateConventionModelBuilder();
modelBuilder.Entity>>().OwnsMany(x => x.JsonbFields, r => r.ToJson());
- ;
VerifyError(
CoreStrings.WarningAsErrorTemplate(