diff --git a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
index 52a3752053c..cde91a000ab 100644
--- a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
+++ b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
@@ -1066,10 +1066,10 @@ or nameof(EntityFrameworkQueryableExtensions.ToListAsync)
method.GetParameters()[1].ParameterType.GenericTypeArguments[0].GenericTypeArguments[1])),
// ExecuteDelete/Update behave just like other scalar-returning operators
- nameof(RelationalQueryableExtensions.ExecuteDeleteAsync) when method.DeclaringType == typeof(RelationalQueryableExtensions)
- => RewriteToSync(typeof(RelationalQueryableExtensions).GetMethod(nameof(RelationalQueryableExtensions.ExecuteDelete))),
- nameof(RelationalQueryableExtensions.ExecuteUpdateAsync) when method.DeclaringType == typeof(RelationalQueryableExtensions)
- => RewriteToSync(typeof(RelationalQueryableExtensions).GetMethod(nameof(RelationalQueryableExtensions.ExecuteUpdate))),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync) when method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
+ => RewriteToSync(typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteDelete))),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync) when method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
+ => RewriteToSync(typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate))),
// In the regular case (sync terminating operator which needs to stay in the query tree), simply compose the terminating
// operator over the penultimate and return that.
diff --git a/src/EFCore.Design/Query/Internal/QueryLocator.cs b/src/EFCore.Design/Query/Internal/QueryLocator.cs
index e8a0c13a339..aa2ed595246 100644
--- a/src/EFCore.Design/Query/Internal/QueryLocator.cs
+++ b/src/EFCore.Design/Query/Internal/QueryLocator.cs
@@ -174,11 +174,11 @@ or nameof(EntityFrameworkQueryableExtensions.SumAsync)
or nameof(EntityFrameworkQueryableExtensions.ForEachAsync)
when IsOnEfQueryableExtensions():
- case nameof(RelationalQueryableExtensions.ExecuteDelete)
- or nameof(RelationalQueryableExtensions.ExecuteUpdate)
- or nameof(RelationalQueryableExtensions.ExecuteDeleteAsync)
- or nameof(RelationalQueryableExtensions.ExecuteUpdateAsync)
- when IsOnEfRelationalQueryableExtensions():
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)
+ or nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)
+ or nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync)
+ or nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)
+ when IsOnEfQueryableExtensions():
if (ProcessQueryCandidate(invocation))
{
return;
@@ -202,9 +202,6 @@ bool IsOnQueryable()
bool IsOnEfQueryableExtensions()
=> IsOnTypeSymbol(_symbols.EfQueryableExtensions);
- bool IsOnEfRelationalQueryableExtensions()
- => IsOnTypeSymbol(_symbols.EfRelationalQueryableExtensions);
-
bool IsOnTypeSymbol(ITypeSymbol typeSymbol)
=> _semanticModel.GetSymbolInfo(invocation, _cancellationToken).Symbol is IMethodSymbol methodSymbol
&& methodSymbol.ContainingType.OriginalDefinition.Equals(typeSymbol, SymbolEqualityComparer.Default);
@@ -342,7 +339,6 @@ private readonly struct Symbols
public readonly INamedTypeSymbol IEnumerableOfT;
public readonly INamedTypeSymbol Queryable;
public readonly INamedTypeSymbol EfQueryableExtensions;
- public readonly INamedTypeSymbol EfRelationalQueryableExtensions;
// ReSharper restore InconsistentNaming
private Symbols(Compilation compilation)
@@ -358,7 +354,6 @@ private Symbols(Compilation compilation)
IEnumerableOfT = GetTypeSymbolOrThrow("System.Collections.Generic.IEnumerable`1");
Queryable = GetTypeSymbolOrThrow("System.Linq.Queryable");
EfQueryableExtensions = GetTypeSymbolOrThrow("Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions");
- EfRelationalQueryableExtensions = GetTypeSymbolOrThrow("Microsoft.EntityFrameworkCore.RelationalQueryableExtensions");
}
public static Symbols Load(Compilation compilation)
diff --git a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs
index 502c8c8ca93..f9b69bcf54e 100644
--- a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs
@@ -272,115 +272,4 @@ internal static readonly MethodInfo AsSplitQueryMethodInfo
= typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(AsSplitQuery))!;
#endregion
-
- #region ExecuteDelete
-
- ///
- /// Deletes all database rows for the entity instances which match the LINQ query from the database.
- ///
- ///
- ///
- /// This operation executes immediately against the database, rather than being deferred until
- /// is called. It also does not interact with the EF change tracker in any way:
- /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
- /// to reflect the changes.
- ///
- ///
- /// See Executing bulk operations with EF Core
- /// for more information and examples.
- ///
- ///
- /// The source query.
- /// The total number of rows deleted in the database.
- public static int ExecuteDelete(this IQueryable source)
- => source.Provider.Execute(Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression));
-
- ///
- /// Asynchronously deletes database rows for the entity instances which match the LINQ query from the database.
- ///
- ///
- ///
- /// This operation executes immediately against the database, rather than being deferred until
- /// is called. It also does not interact with the EF change tracker in any way:
- /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
- /// to reflect the changes.
- ///
- ///
- /// See Executing bulk operations with EF Core
- /// for more information and examples.
- ///
- ///
- /// The source query.
- /// A to observe while waiting for the task to complete.
- /// The total number of rows deleted in the database.
- public static Task ExecuteDeleteAsync(this IQueryable source, CancellationToken cancellationToken = default)
- => source.Provider is IAsyncQueryProvider provider
- ? provider.ExecuteAsync>(
- Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression), cancellationToken)
- : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
-
- internal static readonly MethodInfo ExecuteDeleteMethodInfo
- = typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteDelete))!;
-
- #endregion
-
- #region ExecuteUpdate
-
- ///
- /// Updates all database rows for the entity instances which match the LINQ query from the database.
- ///
- ///
- ///
- /// This operation executes immediately against the database, rather than being deferred until
- /// is called. It also does not interact with the EF change tracker in any way:
- /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
- /// to reflect the changes.
- ///
- ///
- /// See Executing bulk operations with EF Core
- /// for more information and examples.
- ///
- ///
- /// The source query.
- /// A collection of set property statements specifying properties to update.
- /// The total number of rows updated in the database.
- public static int ExecuteUpdate(
- this IQueryable source,
- Expression, SetPropertyCalls>> setPropertyCalls)
- => source.Provider.Execute(
- Expression.Call(ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls));
-
- ///
- /// Asynchronously updates database rows for the entity instances which match the LINQ query from the database.
- ///
- ///
- ///
- /// This operation executes immediately against the database, rather than being deferred until
- /// is called. It also does not interact with the EF change tracker in any way:
- /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
- /// to reflect the changes.
- ///
- ///
- /// See Executing bulk operations with EF Core
- /// for more information and examples.
- ///
- ///
- /// The source query.
- /// A collection of set property statements specifying properties to update.
- /// A to observe while waiting for the task to complete.
- /// The total number of rows updated in the database.
- public static Task ExecuteUpdateAsync(
- this IQueryable source,
- Expression, SetPropertyCalls>> setPropertyCalls,
- CancellationToken cancellationToken = default)
- => source.Provider is IAsyncQueryProvider provider
- ? provider.ExecuteAsync>(
- Expression.Call(
- ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls), cancellationToken)
- : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
-
- internal static readonly MethodInfo ExecuteUpdateMethodInfo
- = typeof(RelationalQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteUpdate))!;
-
- #endregion
}
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index b174634a215..817c3f92fa7 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -1413,14 +1413,6 @@ public static string NonConstantOrParameterAsInExpressionValue(object? type)
public static string NoneRelationalTypeMappingOnARelationalTypeMappingSource
=> GetString("NoneRelationalTypeMappingOnARelationalTypeMappingSource");
- ///
- /// The LINQ expression '{expression}' could not be translated. Additional information: {details} See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
- ///
- public static string NonQueryTranslationFailedWithDetails(object? expression, object? details)
- => string.Format(
- GetString("NonQueryTranslationFailedWithDetails", nameof(expression), nameof(details)),
- expression, details);
-
///
/// Cannot set 'IsNullable' on DbFunction '{functionName}' since the function does not represent a scalar function.
///
@@ -1619,12 +1611,6 @@ public static string SetOperationsRequireAtLeastOneSideWithValidTypeMapping(obje
GetString("SetOperationsRequireAtLeastOneSideWithValidTypeMapping", nameof(setOperationType)),
setOperationType);
- ///
- /// The SetProperty<TProperty> method can only be used within 'ExecuteUpdate' method.
- ///
- public static string SetPropertyMethodInvoked
- => GetString("SetPropertyMethodInvoked");
-
///
/// This LINQ query is being executed in split-query mode, and the SQL shown is for the first query to be executed. Additional queries may also be executed depending on the results of the first query.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index 1e7a548235a..a3e8dae1fe8 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -1,17 +1,17 @@
-
@@ -957,9 +957,6 @@
'FindMapping' was called on a 'RelationalTypeMappingSource' with a non-relational 'TypeMappingInfo'.
-
- The LINQ expression '{expression}' could not be translated. Additional information: {details} See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
-
Cannot set 'IsNullable' on DbFunction '{functionName}' since the function does not represent a scalar function.
@@ -1041,9 +1038,6 @@
A set operation '{setOperationType}' requires valid type mapping for at least one of its sides.
-
- The SetProperty<TProperty> method can only be used within 'ExecuteUpdate' method.
-
This LINQ query is being executed in split-query mode, and the SQL shown is for the first query to be executed. Additional queries may also be executed depending on the results of the first query.
diff --git a/src/EFCore.Relational/Properties/TypeForwards.cs b/src/EFCore.Relational/Properties/TypeForwards.cs
index 7312e1c35c4..8121602abd9 100644
--- a/src/EFCore.Relational/Properties/TypeForwards.cs
+++ b/src/EFCore.Relational/Properties/TypeForwards.cs
@@ -6,3 +6,4 @@
[assembly: TypeForwardedTo(typeof(AttributeCodeFragment))]
[assembly: TypeForwardedTo(typeof(MethodCallCodeFragment))]
[assembly: TypeForwardedTo(typeof(NestedClosureCodeFragment))]
+[assembly: TypeForwardedTo(typeof(SetPropertyCalls<>))]
diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
index 36a605bdab2..af7ed7eebe7 100644
--- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs
+++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
@@ -203,7 +203,8 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteDelete)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)));
}
///
@@ -1540,7 +1541,8 @@ void LiftPredicate(TableExpressionBase joinTable)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteUpdate)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)));
}
///
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
index 0d7ddc80370..a21bdaf7ba5 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
@@ -9,13 +9,8 @@ namespace Microsoft.EntityFrameworkCore.Query;
public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
- ///
- /// Translates method
- /// over the given source.
- ///
- /// The shaped query on which the operator is applied.
- /// The non query after translation.
- protected virtual NonQueryExpression? TranslateExecuteDelete(ShapedQueryExpression source)
+ ///
+ protected override NonQueryExpression? TranslateExecuteDelete(ShapedQueryExpression source)
{
source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));
@@ -29,7 +24,8 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
if (mappingStrategy == RelationalAnnotationNames.TptMappingStrategy)
{
AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnTPT(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ RelationalStrings.ExecuteOperationOnTPT(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
return null;
}
@@ -38,7 +34,8 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
// We allow TPC is it is leaf type
AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnTPC(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ RelationalStrings.ExecuteOperationOnTPC(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
return null;
}
@@ -46,7 +43,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
AddTranslationErrorDetails(
RelationalStrings.ExecuteOperationOnEntitySplitting(
- nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
return null;
}
@@ -97,7 +94,7 @@ static bool AreOtherNonOwnedEntityTypesInTheTable(IEntityType rootType, ITableBa
{
AddTranslationErrorDetails(
RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
- nameof(RelationalQueryableExtensions.ExecuteDelete),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),
entityType.DisplayName()));
return null;
}
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
index 0caec315c1d..b627332a274 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
@@ -14,22 +14,8 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
private static readonly MethodInfo ParameterValueExtractorMethod =
typeof(RelationalSqlTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ParameterValueExtractor))!;
- ///
- /// Translates
- ///
- /// method
- /// over the given source.
- ///
- /// The shaped query on which the operator is applied.
- ///
- /// The lambda expression containing
- ///
- /// statements.
- ///
- /// The non query after translation.
- protected virtual NonQueryExpression? TranslateExecuteUpdate(ShapedQueryExpression source, LambdaExpression setPropertyCalls)
+ ///
+ protected override NonQueryExpression? TranslateExecuteUpdate(ShapedQueryExpression source, LambdaExpression setPropertyCalls)
{
// Our source may have IncludeExpressions because of owned entities or auto-include; unwrap these, as they're meaningless for
// ExecuteUpdate's lambdas. Note that we don't currently support updates across tables.
@@ -62,7 +48,7 @@ public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
AddTranslationErrorDetails(
RelationalStrings.ExecuteOperationOnTPC(
- nameof(RelationalQueryableExtensions.ExecuteUpdate), tpcTablesExpression.EntityType.DisplayName()));
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate), tpcTablesExpression.EntityType.DisplayName()));
return null;
}
@@ -423,7 +409,7 @@ MethodCallExpression mce when mce.TryGetIndexerArguments(RelationalDependencies.
{
AddTranslationErrorDetails(
RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
- nameof(RelationalQueryableExtensions.ExecuteUpdate),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate),
entityType.DisplayName()));
return null;
}
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index 20115136354..f2e9c167e70 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -230,31 +230,6 @@ [new FromSqlExpression(alias, sqlQueryRootExpression.Sql, sqlQueryRootExpression
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
var method = methodCallExpression.Method;
- if (method.DeclaringType == typeof(RelationalQueryableExtensions))
- {
- var source = Visit(methodCallExpression.Arguments[0]);
- if (source is ShapedQueryExpression shapedQueryExpression)
- {
- var genericMethod = method.IsGenericMethod ? method.GetGenericMethodDefinition() : null;
- switch (method.Name)
- {
- case nameof(RelationalQueryableExtensions.ExecuteDelete)
- when genericMethod == RelationalQueryableExtensions.ExecuteDeleteMethodInfo:
- return TranslateExecuteDelete(shapedQueryExpression)
- ?? throw new InvalidOperationException(
- RelationalStrings.NonQueryTranslationFailedWithDetails(
- methodCallExpression.Print(), TranslationErrorDetails));
-
- case nameof(RelationalQueryableExtensions.ExecuteUpdate)
- when genericMethod == RelationalQueryableExtensions.ExecuteUpdateMethodInfo:
- return TranslateExecuteUpdate(shapedQueryExpression, methodCallExpression.Arguments[1].UnwrapLambdaFromQuote())
- ?? throw new InvalidOperationException(
- RelationalStrings.NonQueryTranslationFailedWithDetails(
- methodCallExpression.Print(), TranslationErrorDetails));
- }
- }
- }
-
var translated = base.VisitMethodCall(methodCallExpression);
// For Contains over a collection parameter, if the provider hasn't implemented TranslateCollection (e.g. OPENJSON on SQL
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
index 717d31dad4f..b33dac3340e 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
@@ -95,7 +95,8 @@ protected override Expression VisitDelete(DeleteExpression deleteExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteDelete)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)));
}
///
@@ -169,7 +170,8 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
}
throw new InvalidOperationException(
- RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(nameof(RelationalQueryableExtensions.ExecuteUpdate)));
+ RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)));
}
///
diff --git a/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs b/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
index 3dbcaed6b29..665b828fbe4 100644
--- a/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
+++ b/src/EFCore/Extensions/EntityFrameworkQueryableExtensions.cs
@@ -2912,8 +2912,8 @@ source.Provider is EntityQueryProvider
///
public static IQueryable TagWithCallSite(
this IQueryable source,
- [NotParameterized][CallerFilePath] string? filePath = null,
- [NotParameterized][CallerLineNumber] int lineNumber = 0)
+ [NotParameterized] [CallerFilePath] string? filePath = null,
+ [NotParameterized] [CallerLineNumber] int lineNumber = 0)
=> source.Provider is EntityQueryProvider
? source.Provider.CreateQuery(
Expression.Call(
@@ -3266,4 +3266,115 @@ private static TResult ExecuteAsync(
operatorMethodInfo, source, (Expression?)null, cancellationToken);
#endregion
+
+ #region ExecuteDelete
+
+ ///
+ /// Deletes all database rows for the entity instances which match the LINQ query from the database.
+ ///
+ ///
+ ///
+ /// This operation executes immediately against the database, rather than being deferred until
+ /// is called. It also does not interact with the EF change tracker in any way:
+ /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
+ /// to reflect the changes.
+ ///
+ ///
+ /// See Executing bulk operations with EF Core
+ /// for more information and examples.
+ ///
+ ///
+ /// The source query.
+ /// The total number of rows deleted in the database.
+ public static int ExecuteDelete(this IQueryable source)
+ => source.Provider.Execute(Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression));
+
+ ///
+ /// Asynchronously deletes database rows for the entity instances which match the LINQ query from the database.
+ ///
+ ///
+ ///
+ /// This operation executes immediately against the database, rather than being deferred until
+ /// is called. It also does not interact with the EF change tracker in any way:
+ /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
+ /// to reflect the changes.
+ ///
+ ///
+ /// See Executing bulk operations with EF Core
+ /// for more information and examples.
+ ///
+ ///
+ /// The source query.
+ /// A to observe while waiting for the task to complete.
+ /// The total number of rows deleted in the database.
+ public static Task ExecuteDeleteAsync(this IQueryable source, CancellationToken cancellationToken = default)
+ => source.Provider is IAsyncQueryProvider provider
+ ? provider.ExecuteAsync>(
+ Expression.Call(ExecuteDeleteMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression), cancellationToken)
+ : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
+
+ internal static readonly MethodInfo ExecuteDeleteMethodInfo
+ = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteDelete))!;
+
+ #endregion
+
+ #region ExecuteUpdate
+
+ ///
+ /// Updates all database rows for the entity instances which match the LINQ query from the database.
+ ///
+ ///
+ ///
+ /// This operation executes immediately against the database, rather than being deferred until
+ /// is called. It also does not interact with the EF change tracker in any way:
+ /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
+ /// to reflect the changes.
+ ///
+ ///
+ /// See Executing bulk operations with EF Core
+ /// for more information and examples.
+ ///
+ ///
+ /// The source query.
+ /// A collection of set property statements specifying properties to update.
+ /// The total number of rows updated in the database.
+ public static int ExecuteUpdate(
+ this IQueryable source,
+ Expression, SetPropertyCalls>> setPropertyCalls)
+ => source.Provider.Execute(
+ Expression.Call(ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls));
+
+ ///
+ /// Asynchronously updates database rows for the entity instances which match the LINQ query from the database.
+ ///
+ ///
+ ///
+ /// This operation executes immediately against the database, rather than being deferred until
+ /// is called. It also does not interact with the EF change tracker in any way:
+ /// entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated
+ /// to reflect the changes.
+ ///
+ ///
+ /// See Executing bulk operations with EF Core
+ /// for more information and examples.
+ ///
+ ///
+ /// The source query.
+ /// A collection of set property statements specifying properties to update.
+ /// A to observe while waiting for the task to complete.
+ /// The total number of rows updated in the database.
+ public static Task ExecuteUpdateAsync(
+ this IQueryable source,
+ Expression, SetPropertyCalls>> setPropertyCalls,
+ CancellationToken cancellationToken = default)
+ => source.Provider is IAsyncQueryProvider provider
+ ? provider.ExecuteAsync>(
+ Expression.Call(
+ ExecuteUpdateMethodInfo.MakeGenericMethod(typeof(TSource)), source.Expression, setPropertyCalls), cancellationToken)
+ : throw new InvalidOperationException(CoreStrings.IQueryableProviderNotAsync);
+
+ internal static readonly MethodInfo ExecuteUpdateMethodInfo
+ = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(ExecuteUpdate))!;
+
+ #endregion
}
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 317fe454ffa..d190abe69e6 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -1140,6 +1140,14 @@ public static string ErrorMaterializingPropertyInvalidCast(object? entityType, o
GetString("ErrorMaterializingPropertyInvalidCast", nameof(entityType), nameof(property), nameof(expectedType), nameof(actualType)),
entityType, property, expectedType, actualType);
+ ///
+ /// The methods '{methodName}' and '{asyncMethodName}' are not supported by the current database provider. Please contact the publisher of the database provider for more information.
+ ///
+ public static string ExecuteQueriesNotSupported(object? methodName, object? asyncMethodName)
+ => string.Format(
+ GetString("ExecuteQueriesNotSupported", nameof(methodName), nameof(asyncMethodName)),
+ methodName, asyncMethodName);
+
///
/// The configured execution strategy '{strategy}' does not support user-initiated transactions. Use the execution strategy returned by '{getExecutionStrategyMethod}' to execute all the operations in the transaction as a retriable unit.
///
@@ -2137,6 +2145,14 @@ public static string NonIndexerEntityType(object? property, object? entityType,
GetString("NonIndexerEntityType", nameof(property), nameof(entityType), nameof(type)),
property, entityType, type);
+ ///
+ /// The LINQ expression '{expression}' could not be translated. Additional information: {details} See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
+ ///
+ public static string NonQueryTranslationFailedWithDetails(object? expression, object? details)
+ => string.Format(
+ GetString("NonQueryTranslationFailedWithDetails", nameof(expression), nameof(details)),
+ expression, details);
+
///
/// The collection type '{2_collectionType}' being used for navigation '{1_entityType}.{0_navigation}' does not implement 'INotifyCollectionChanged'. Any entity type configured to use the '{changeTrackingStrategy}' change tracking strategy must use collections that implement 'INotifyCollectionChanged'. Consider using 'ObservableCollection<T>' for this.
///
@@ -2907,6 +2923,12 @@ public static string ServiceProviderConfigRemoved(object? key)
public static string SetOperationWithDifferentIncludesInOperands
=> GetString("SetOperationWithDifferentIncludesInOperands");
+ ///
+ /// The SetProperty<TProperty> method can only be used within 'ExecuteUpdate' method.
+ ///
+ public static string SetPropertyMethodInvoked
+ => GetString("SetPropertyMethodInvoked");
+
///
/// The shared-type entity type '{entityType}' cannot have a base type.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index eb2d6b4c5ac..d271b9fec11 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -546,6 +546,9 @@
An error occurred while reading a database value for property '{entityType}.{property}'. The expected type was '{expectedType}' but the actual value was of type '{actualType}'.
+
+ The methods '{methodName}' and '{asyncMethodName}' are not supported by the current database provider. Please contact the publisher of the database provider for more information.
+
The configured execution strategy '{strategy}' does not support user-initiated transactions. Use the execution strategy returned by '{getExecutionStrategyMethod}' to execute all the operations in the transaction as a retriable unit.
@@ -1258,6 +1261,9 @@
The collection type '{2_collectionType}' being used for navigation '{1_entityType}.{0_navigation}' does not implement 'INotifyCollectionChanged'. Any entity type configured to use the '{changeTrackingStrategy}' change tracking strategy must use collections that implement 'INotifyCollectionChanged'. Consider using 'ObservableCollection<T>' for this.
+
+ The LINQ expression '{expression}' could not be translated. Additional information: {details} See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
+
The foreign key {foreignKeyProperties} on the entity type '{declaringEntityType}' cannot have a required dependent end since it is not unique.
@@ -1558,6 +1564,9 @@
Unable to translate set operation since both operands have different 'Include' operations. Consider having same 'Include' applied on both sides.
+
+ The SetProperty<TProperty> method can only be used within 'ExecuteUpdate' method.
+
The shared-type entity type '{entityType}' cannot have a base type.
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
index 45b85cf0962..f7e8de8764c 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
@@ -555,6 +555,13 @@ when QueryableMethods.IsSumWithSelector(method):
thenInclude: false,
setLoaded: false);
+ // Handled by relational/provider even though method is on `EntityFrameworkQueryableExtensions`
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteDelete):
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync):
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate):
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync):
+ return ProcessUnknownMethod(methodCallExpression);
+
case nameof(Queryable.GroupBy)
when genericMethod == QueryableMethods.GroupByWithKeySelector:
return ProcessGroupBy(
diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
index 7f48d4d6559..4b33dfe6311 100644
--- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
@@ -138,6 +138,32 @@ protected override Expression VisitExtension(Expression extensionExpression)
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
var method = methodCallExpression.Method;
+
+ if (method.DeclaringType == typeof(EntityFrameworkQueryableExtensions))
+ {
+ var source = Visit(methodCallExpression.Arguments[0]);
+ if (source is ShapedQueryExpression shapedQueryExpression)
+ {
+ var genericMethod = method.IsGenericMethod ? method.GetGenericMethodDefinition() : null;
+ switch (method.Name)
+ {
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteDelete)
+ when genericMethod == EntityFrameworkQueryableExtensions.ExecuteDeleteMethodInfo:
+ return TranslateExecuteDelete(shapedQueryExpression)
+ ?? throw new InvalidOperationException(
+ CoreStrings.NonQueryTranslationFailedWithDetails(
+ methodCallExpression.Print(), TranslationErrorDetails));
+
+ case nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate)
+ when genericMethod == EntityFrameworkQueryableExtensions.ExecuteUpdateMethodInfo:
+ return TranslateExecuteUpdate(shapedQueryExpression, methodCallExpression.Arguments[1].UnwrapLambdaFromQuote())
+ ?? throw new InvalidOperationException(
+ CoreStrings.NonQueryTranslationFailedWithDetails(
+ methodCallExpression.Print(), TranslationErrorDetails));
+ }
+ }
+ }
+
if (method.DeclaringType == typeof(Queryable)
|| method.DeclaringType == typeof(QueryableExtensions))
{
@@ -1000,4 +1026,39 @@ protected abstract ShapedQueryExpression TranslateSelect(
=> null;
#endregion Queryable collection support
+
+ #region ExecuteUpdate/ExecuteDelete
+
+ ///
+ /// Translates method
+ /// over the given source.
+ ///
+ /// The shaped query on which the operator is applied.
+ /// The non query after translation.
+ protected virtual Expression? TranslateExecuteDelete(ShapedQueryExpression source)
+ => throw new InvalidOperationException(
+ CoreStrings.ExecuteQueriesNotSupported(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync)));
+
+ ///
+ /// Translates
+ ///
+ /// method
+ /// over the given source.
+ ///
+ /// The shaped query on which the operator is applied.
+ ///
+ /// The lambda expression containing
+ ///
+ /// statements.
+ ///
+ /// The non query after translation.
+ protected virtual Expression? TranslateExecuteUpdate(ShapedQueryExpression source, LambdaExpression setPropertyCalls)
+ => throw new InvalidOperationException(
+ CoreStrings.ExecuteQueriesNotSupported(
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate), nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync)));
+
+ #endregion ExecuteUpdate/ExecuteDelete
}
diff --git a/src/EFCore.Relational/Query/SetPropertyCalls.cs b/src/EFCore/Query/SetPropertyCalls.cs
similarity index 95%
rename from src/EFCore.Relational/Query/SetPropertyCalls.cs
rename to src/EFCore/Query/SetPropertyCalls.cs
index 6508452059c..d525aa8184f 100644
--- a/src/EFCore.Relational/Query/SetPropertyCalls.cs
+++ b/src/EFCore/Query/SetPropertyCalls.cs
@@ -40,7 +40,7 @@ private SetPropertyCalls()
public SetPropertyCalls SetProperty(
Func propertyExpression,
Func valueExpression)
- => throw new InvalidOperationException(RelationalStrings.SetPropertyMethodInvoked);
+ => throw new InvalidOperationException(CoreStrings.SetPropertyMethodInvoked);
///
/// Specifies a property and corresponding value it should be updated to in ExecuteUpdate method.
@@ -55,7 +55,7 @@ public SetPropertyCalls SetProperty(
public SetPropertyCalls SetProperty(
Func propertyExpression,
TProperty valueExpression)
- => throw new InvalidOperationException(RelationalStrings.SetPropertyMethodInvoked);
+ => throw new InvalidOperationException(CoreStrings.SetPropertyMethodInvoked);
#region Hidden System.Object members
diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs
index 666a22e8a8e..51e6af0b2ad 100644
--- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.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 Microsoft.EntityFrameworkCore.BulkUpdates;
+
namespace Microsoft.EntityFrameworkCore;
public class InMemoryComplianceTest : ComplianceTestBase
@@ -16,6 +18,12 @@ public class InMemoryComplianceTest : ComplianceTestBase
typeof(StoreGeneratedTestBase<>),
typeof(ConferencePlannerTestBase<>),
typeof(ManyToManyQueryTestBase<>),
+ typeof(ComplexTypeBulkUpdatesTestBase<>),
+ typeof(BulkUpdatesTestBase<>),
+ typeof(FiltersInheritanceBulkUpdatesTestBase<>),
+ typeof(InheritanceBulkUpdatesTestBase<>),
+ typeof(NonSharedModelBulkUpdatesTestBase),
+ typeof(NorthwindBulkUpdatesTestBase<>),
};
protected override Assembly TargetAssembly { get; } = typeof(InMemoryComplianceTest).Assembly;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalFixtureBase.cs
new file mode 100644
index 00000000000..8337d339a05
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalFixtureBase.cs
@@ -0,0 +1,26 @@
+// 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.BulkUpdates;
+
+#nullable disable
+
+public abstract class ComplexTypeBulkUpdatesRelationalFixtureBase : ComplexTypeBulkUpdatesFixtureBase, ITestSqlLoggerFactory
+{
+ public override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
+
+ public new RelationalTestStore TestStore
+ => (RelationalTestStore)base.TestStore;
+
+ public TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => base.AddOptions(builder).ConfigureWarnings(
+ c => c.Log(RelationalEventId.QueryPossibleUnintendedUseOfEqualsWarning))
+ .EnableDetailedErrors();
+
+ protected override bool ShouldLogCategory(string logCategory)
+ => logCategory == DbLoggerCategory.Query.Name;
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalTestBase.cs
new file mode 100644
index 00000000000..72edb033356
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesRelationalTestBase.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.BulkUpdates;
+
+#nullable disable
+
+public abstract class ComplexTypeBulkUpdatesRelationalTestBase : ComplexTypeBulkUpdatesTestBase
+ where TFixture : ComplexTypeBulkUpdatesRelationalFixtureBase, new()
+{
+ protected ComplexTypeBulkUpdatesRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ ClearLog();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ public override Task Delete_complex_type(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteDeleteOnNonEntityType,
+ () => base.Delete_complex_type(async));
+
+ public override Task Update_projected_complex_type_via_OrderBy_Skip(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteUpdateSubqueryNotSupportedOverComplexTypes("Customer.ShippingAddress#Address"),
+ () => base.Update_projected_complex_type_via_OrderBy_Skip(async));
+
+ protected static async Task AssertTranslationFailed(string details, Func query)
+ => Assert.Contains(
+ CoreStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
+ (await Assert.ThrowsAsync(query)).Message);
+
+ private void ClearLog()
+ => Fixture.TestSqlLoggerFactory.Clear();
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesRelationalTestBase.cs
new file mode 100644
index 00000000000..ca28343d386
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesRelationalTestBase.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestModels.InheritanceModel;
+
+namespace Microsoft.EntityFrameworkCore.BulkUpdates;
+
+#nullable disable
+
+public abstract class FiltersInheritanceBulkUpdatesRelationalTestBase : FiltersInheritanceBulkUpdatesTestBase
+ where TFixture : InheritanceBulkUpdatesRelationalFixtureBase, new()
+{
+ protected FiltersInheritanceBulkUpdatesRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ ClearLog();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteDelete", "EagleQuery"),
+ () => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => e.CountryId > 0),
+ rowsAffectedCount: 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteUpdate", "EagleQuery"),
+ () => AssertUpdate(
+ async,
+ ss => ss.Set().Where(e => e.CountryId > 0),
+ e => e,
+ s => s.SetProperty(e => e.Name, "Eagle"),
+ rowsAffectedCount: 1));
+
+ protected static async Task AssertTranslationFailed(string details, Func query)
+ => Assert.Contains(
+ CoreStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
+ (await Assert.ThrowsAsync(query)).Message);
+
+ protected abstract void ClearLog();
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalFixtureBase.cs
new file mode 100644
index 00000000000..8517405afb8
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalFixtureBase.cs
@@ -0,0 +1,26 @@
+// 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.BulkUpdates;
+
+#nullable disable
+
+public abstract class InheritanceBulkUpdatesRelationalFixtureBase : InheritanceBulkUpdatesFixtureBase, ITestSqlLoggerFactory
+{
+ public override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
+
+ public new RelationalTestStore TestStore
+ => (RelationalTestStore)base.TestStore;
+
+ public TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => base.AddOptions(builder).ConfigureWarnings(
+ c => c.Log(RelationalEventId.QueryPossibleUnintendedUseOfEqualsWarning))
+ .EnableDetailedErrors();
+
+ protected override bool ShouldLogCategory(string logCategory)
+ => logCategory == DbLoggerCategory.Query.Name;
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalTestBase.cs
new file mode 100644
index 00000000000..5e0856e8699
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesRelationalTestBase.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestModels.InheritanceModel;
+
+namespace Microsoft.EntityFrameworkCore.BulkUpdates;
+
+#nullable disable
+
+public abstract class InheritanceBulkUpdatesRelationalTestBase : InheritanceBulkUpdatesTestBase
+ where TFixture : InheritanceBulkUpdatesRelationalFixtureBase, new()
+{
+ protected InheritanceBulkUpdatesRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ ClearLog();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteDelete", "EagleQuery"),
+ () => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => e.CountryId > 0),
+ rowsAffectedCount: 1));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteUpdate", "EagleQuery"),
+ () => AssertUpdate(
+ async,
+ ss => ss.Set().Where(e => e.CountryId > 0),
+ e => e,
+ s => s.SetProperty(e => e.Name, "Eagle"),
+ rowsAffectedCount: 1));
+
+ protected static async Task AssertTranslationFailed(string details, Func query)
+ => Assert.Contains(
+ CoreStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
+ (await Assert.ThrowsAsync(query)).Message);
+
+ protected virtual void ClearLog()
+ => Fixture.TestSqlLoggerFactory.Clear();
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesRelationalTestBase.cs
new file mode 100644
index 00000000000..c3dc5ad63d8
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesRelationalTestBase.cs
@@ -0,0 +1,104 @@
+// 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.BulkUpdates;
+
+#nullable disable
+
+public abstract class NonSharedModelBulkUpdatesRelationalTestBase : NonSharedModelBulkUpdatesTestBase
+{
+ protected override string StoreName
+ => "NonSharedModelBulkUpdatesTests";
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Delete_aggregate_root_when_table_sharing_with_non_owned_throws(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: mb =>
+ {
+ mb.Entity().HasOne().WithOne().HasForeignKey(e => e.Id);
+ mb.Entity().ToTable(nameof(Owner));
+ });
+
+ await AssertTranslationFailedWithDetails(
+ () => AssertDelete(
+ async, contextFactory.CreateContext,
+ context => context.Set(), rowsAffectedCount: 0),
+ RelationalStrings.ExecuteDeleteOnTableSplitting(nameof(Owner)));
+ }
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Update_main_table_in_entity_with_entity_splitting(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: mb => mb.Entity()
+ .ToTable("Blogs")
+ .SplitToTable(
+ "BlogsPart1", tb =>
+ {
+ tb.Property(b => b.Title);
+ tb.Property(b => b.Rating);
+ }),
+ seed: async context =>
+ {
+ context.Set().Add(new Blog { Title = "SomeBlog" });
+ await context.SaveChangesAsync();
+ });
+
+ await AssertUpdate(
+ async,
+ contextFactory.CreateContext,
+ ss => ss.Set(),
+ s => s.SetProperty(b => b.CreationTimestamp, b => new DateTime(2020, 1, 1)),
+ rowsAffectedCount: 1);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Update_non_main_table_in_entity_with_entity_splitting(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: mb => mb.Entity()
+ .ToTable("Blogs")
+ .SplitToTable(
+ "BlogsPart1", tb =>
+ {
+ tb.Property(b => b.Title);
+ tb.Property(b => b.Rating);
+ }),
+ seed: async context =>
+ {
+ context.Set().Add(new Blog { Title = "SomeBlog" });
+ await context.SaveChangesAsync();
+ });
+
+ await AssertUpdate(
+ async,
+ contextFactory.CreateContext,
+ ss => ss.Set(),
+ s => s
+ .SetProperty(b => b.Title, b => b.Rating.ToString())
+ .SetProperty(b => b.Rating, b => b.Title!.Length),
+ rowsAffectedCount: 1);
+ }
+
+ #region HelperMethods
+
+ protected static async Task AssertTranslationFailedWithDetails(Func query, string details)
+ => Assert.Contains(
+ CoreStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
+ (await Assert.ThrowsAsync(query))
+ .Message);
+
+ public override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
+
+ protected TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ protected void ClearLog()
+ => TestSqlLoggerFactory.Clear();
+
+ #endregion
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalFixture.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalFixture.cs
new file mode 100644
index 00000000000..3ea28305a1f
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalFixture.cs
@@ -0,0 +1,27 @@
+// 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.BulkUpdates;
+
+#nullable disable
+
+public abstract class NorthwindBulkUpdatesRelationalFixture : NorthwindBulkUpdatesFixture, ITestSqlLoggerFactory
+ where TModelCustomizer : ITestModelCustomizer, new()
+{
+ public new RelationalTestStore TestStore
+ => (RelationalTestStore)base.TestStore;
+
+ public TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => base.AddOptions(builder).ConfigureWarnings(
+ c => c.Log(RelationalEventId.QueryPossibleUnintendedUseOfEqualsWarning))
+ .EnableDetailedErrors();
+
+ protected override bool ShouldLogCategory(string logCategory)
+ => logCategory == DbLoggerCategory.Query.Name;
+
+ public override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
+ => facade.UseTransaction(transaction.GetDbTransaction());
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs
new file mode 100644
index 00000000000..8d19ed1b153
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs
@@ -0,0 +1,117 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestModels.Northwind;
+
+namespace Microsoft.EntityFrameworkCore.BulkUpdates;
+
+#nullable disable
+
+public abstract class NorthwindBulkUpdatesRelationalTestBase : NorthwindBulkUpdatesTestBase
+ where TFixture : NorthwindBulkUpdatesRelationalFixture, new()
+{
+ protected NorthwindBulkUpdatesRelationalTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ ClearLog();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ public override Task Delete_non_entity_projection(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteDeleteOnNonEntityType,
+ () => base.Delete_non_entity_projection(async));
+
+ public override Task Delete_non_entity_projection_2(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteDeleteOnNonEntityType,
+ () => base.Delete_non_entity_projection_2(async));
+
+ public override Task Delete_non_entity_projection_3(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteDeleteOnNonEntityType,
+ () => base.Delete_non_entity_projection_3(async));
+
+ public virtual Task Delete_FromSql_converted_to_subquery(bool async)
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ () => Fixture.CreateContext(),
+ (facade, transaction) => Fixture.UseTransaction(facade, transaction),
+ async context =>
+ {
+ var queryable = context.Set().FromSqlRaw(
+ NormalizeDelimitersInRawString(
+ @"SELECT [OrderID], [ProductID], [UnitPrice], [Quantity], [Discount]
+FROM [Order Details]
+WHERE [OrderID] < 10300"));
+
+ if (async)
+ {
+ await queryable.ExecuteDeleteAsync();
+ }
+ else
+ {
+ queryable.ExecuteDelete();
+ }
+ });
+
+ public override Task Update_without_property_to_set_throws(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.NoSetPropertyInvocation,
+ () => base.Update_without_property_to_set_throws(async));
+
+ public override Task Update_with_invalid_lambda_throws(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.InvalidArgumentToExecuteUpdate,
+ () => base.Update_with_invalid_lambda_throws(async));
+
+ public override Task Update_with_invalid_lambda_in_set_property_throws(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.InvalidPropertyInSetProperty(
+ new ExpressionPrinter().PrintExpression((OrderDetail e) => e.MaybeScalar(e => e.OrderID))),
+ () => base.Update_with_invalid_lambda_in_set_property_throws(async));
+
+ public override Task Update_multiple_tables_throws(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.MultipleTablesInExecuteUpdate("c => c.Customer.ContactName", "c => c.e.OrderDate"),
+ () => base.Update_multiple_tables_throws(async));
+
+ public override Task Update_unmapped_property_throws(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.InvalidPropertyInSetProperty("c => c.IsLondon"),
+ () => base.Update_unmapped_property_throws(async));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_FromSql_set_constant(bool async)
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ () => Fixture.CreateContext(),
+ (facade, transaction) => Fixture.UseTransaction(facade, transaction),
+ async context =>
+ {
+ var queryable = context.Set().FromSqlRaw(
+ NormalizeDelimitersInRawString(
+ @"SELECT [Region], [PostalCode], [Phone], [Fax], [CustomerID], [Country], [ContactTitle], [ContactName], [CompanyName], [City], [Address]
+FROM [Customers]
+WHERE [CustomerID] LIKE 'A%'"));
+
+ if (async)
+ {
+ await queryable.ExecuteUpdateAsync(s => s.SetProperty(c => c.ContactName, "Updated"));
+ }
+ else
+ {
+ queryable.ExecuteUpdate(s => s.SetProperty(c => c.ContactName, "Updated"));
+ }
+ });
+
+ protected static async Task AssertTranslationFailed(string details, Func query)
+ => Assert.Contains(
+ CoreStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
+ (await Assert.ThrowsAsync(query)).Message);
+
+ protected string NormalizeDelimitersInRawString(string sql)
+ => Fixture.TestStore.NormalizeDelimitersInRawString(sql);
+
+ protected virtual void ClearLog()
+ => Fixture.TestSqlLoggerFactory.Clear();
+}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
index 001f6301af0..e838bdda9c9 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
@@ -5,16 +5,10 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPCFiltersInheritanceBulkUpdatesTestBase : FiltersInheritanceBulkUpdatesTestBase
+public abstract class TPCFiltersInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : FiltersInheritanceBulkUpdatesRelationalTestBase(fixture, testOutputHelper)
where TFixture : TPCInheritanceBulkUpdatesFixture, new()
{
- protected TPCFiltersInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
// Keyless entities are mapped as TPH only
public override Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
=> Task.CompletedTask;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesFixture.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesFixture.cs
index cbcd81f4364..e7d6bbbe2d5 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesFixture.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesFixture.cs
@@ -7,14 +7,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPCInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesFixtureBase, ITestSqlLoggerFactory
+public abstract class TPCInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesRelationalFixtureBase
{
protected override string StoreName
=> "TPCInheritanceBulkUpdatesTest";
- public TestSqlLoggerFactory TestSqlLoggerFactory
- => (TestSqlLoggerFactory)ListLoggerFactory;
-
public override bool HasDiscriminator
=> false;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
index ba35a7c8dd0..2a847ac5cc0 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
@@ -5,11 +5,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPCInheritanceBulkUpdatesTestBase : InheritanceBulkUpdatesTestBase
+public abstract class TPCInheritanceBulkUpdatesTestBase : InheritanceBulkUpdatesRelationalTestBase
where TFixture : TPCInheritanceBulkUpdatesFixture, new()
{
protected TPCInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
+ : base(fixture, testOutputHelper)
{
ClearLog();
Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesFixture.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesFixture.cs
index a38b366ecda..5e0cac5e5eb 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesFixture.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesFixture.cs
@@ -7,14 +7,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPHInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesFixtureBase, ITestSqlLoggerFactory
+public abstract class TPHInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesRelationalFixtureBase
{
protected override string StoreName
=> "TPHInheritanceBulkUpdatesTest";
- public TestSqlLoggerFactory TestSqlLoggerFactory
- => (TestSqlLoggerFactory)ListLoggerFactory;
-
// #31378
public override bool EnableComplexTypes
=> false;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesTestBase.cs
index 5c12d13923d..30690134732 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPHInheritanceBulkUpdatesTestBase.cs
@@ -5,11 +5,6 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPHInheritanceBulkUpdatesTestBase : InheritanceBulkUpdatesTestBase
- where TFixture : InheritanceBulkUpdatesFixtureBase, new()
-{
- protected TPHInheritanceBulkUpdatesTestBase(TFixture fixture)
- : base(fixture)
- {
- }
-}
+public abstract class TPHInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : InheritanceBulkUpdatesRelationalTestBase(fixture, testOutputHelper)
+ where TFixture : InheritanceBulkUpdatesRelationalFixtureBase, new();
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
index 82dff763d9c..0d54fee9c1e 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
@@ -5,16 +5,10 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPTFiltersInheritanceBulkUpdatesTestBase : FiltersInheritanceBulkUpdatesTestBase
+public abstract class TPTFiltersInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
+ : FiltersInheritanceBulkUpdatesRelationalTestBase(fixture, testOutputHelper)
where TFixture : TPTInheritanceBulkUpdatesFixture, new()
{
- protected TPTFiltersInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
// Keyless entities are mapped as TPH only
public override Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
=> Task.CompletedTask;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesFixture.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesFixture.cs
index 98c89d228e0..7a2a4795dc9 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesFixture.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesFixture.cs
@@ -7,14 +7,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPTInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesFixtureBase, ITestSqlLoggerFactory
+public abstract class TPTInheritanceBulkUpdatesFixture : InheritanceBulkUpdatesRelationalFixtureBase
{
protected override string StoreName
=> "TPTInheritanceBulkUpdatesTest";
- public TestSqlLoggerFactory TestSqlLoggerFactory
- => (TestSqlLoggerFactory)ListLoggerFactory;
-
public override bool HasDiscriminator
=> false;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
index 0d527401e39..f6a8c22ad69 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
@@ -5,11 +5,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class TPTInheritanceBulkUpdatesTestBase : InheritanceBulkUpdatesTestBase
+public abstract class TPTInheritanceBulkUpdatesTestBase : InheritanceBulkUpdatesRelationalTestBase
where TFixture : TPTInheritanceBulkUpdatesFixture, new()
{
protected TPTInheritanceBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
+ : base(fixture, testOutputHelper)
{
Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
ClearLog();
diff --git a/test/EFCore.Relational.Specification.Tests/EntitySplittingTestBase.cs b/test/EFCore.Relational.Specification.Tests/EntitySplittingTestBase.cs
index 2303856c50e..353419c20ec 100644
--- a/test/EFCore.Relational.Specification.Tests/EntitySplittingTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/EntitySplittingTestBase.cs
@@ -51,7 +51,7 @@ await TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
UseTransaction,
async context => Assert.Contains(
- RelationalStrings.NonQueryTranslationFailedWithDetails(
+ CoreStrings.NonQueryTranslationFailedWithDetails(
"", RelationalStrings.ExecuteOperationOnEntitySplitting("ExecuteDelete", "MeterReading"))[21..],
(await Assert.ThrowsAsync(
async () =>
diff --git a/test/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs b/test/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs
index 293a1cec9ab..04bec022b59 100644
--- a/test/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/TableSplittingTestBase.cs
@@ -768,7 +768,7 @@ await TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
UseTransaction,
async context => Assert.Contains(
- RelationalStrings.NonQueryTranslationFailedWithDetails(
+ CoreStrings.NonQueryTranslationFailedWithDetails(
"", RelationalStrings.ExecuteDeleteOnTableSplitting("Vehicles"))[21..],
(await Assert.ThrowsAsync(
async () =>
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs
similarity index 84%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs
index f0b5d8834b1..8b53b43414a 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/BulkUpdatesTestBase.cs
@@ -38,9 +38,4 @@ public Task AssertUpdate(
Action, IReadOnlyList> asserter = null)
where TResult : class
=> BulkUpdatesAsserter.AssertUpdate(async, query, entitySelector, setPropertyCalls, rowsAffectedCount, asserter);
-
- protected static async Task AssertTranslationFailed(string details, Func query)
- => Assert.Contains(
- RelationalStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
- (await Assert.ThrowsAsync(query)).Message);
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs
similarity index 63%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs
index f77cc45158e..fc2c4342629 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesFixtureBase.cs
@@ -5,11 +5,10 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class ComplexTypeBulkUpdatesFixtureBase : ComplexTypeQueryRelationalFixtureBase, IBulkUpdatesFixtureBase
+public abstract class ComplexTypeBulkUpdatesFixtureBase : ComplexTypeQueryFixtureBase, IBulkUpdatesFixtureBase
{
protected override string StoreName
=> "ComplexTypeBulkUpdatesTest";
- public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
- => facade.UseTransaction(transaction.GetDbTransaction());
+ public abstract void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction);
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
similarity index 85%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
index 10d6cda20f4..9c5224e2fa6 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
@@ -7,15 +7,26 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class ComplexTypeBulkUpdatesTestBase : BulkUpdatesTestBase
+public abstract class ComplexTypeBulkUpdatesTestBase(TFixture fixture) : BulkUpdatesTestBase(fixture)
where TFixture : ComplexTypeBulkUpdatesFixtureBase, new()
{
- protected ComplexTypeBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_complex_type(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Select(c => c.ShippingAddress),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_projected_complex_type_via_OrderBy_Skip(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Select(c => c.ShippingAddress).OrderBy(a => a.ZipCode).Skip(1),
+ a => a,
+ s => s.SetProperty(c => c.ZipCode, 12345),
+ rowsAffectedCount: 3);
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
@@ -25,16 +36,6 @@ public virtual Task Delete_entity_type_with_complex_type(bool async)
ss => ss.Set().Where(e => e.Name == "Monty Elias"),
rowsAffectedCount: 1);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_complex_type_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteDeleteOnNonEntityType,
- () => AssertDelete(
- async,
- ss => ss.Set().Select(c => c.ShippingAddress),
- rowsAffectedCount: 0));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_property_inside_complex_type(bool async)
@@ -96,18 +97,6 @@ public virtual Task Update_multiple_projected_complex_types_via_anonymous_type(b
.SetProperty(x => x.BillingAddress.ZipCode, 54321),
rowsAffectedCount: 3);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteUpdateSubqueryNotSupportedOverComplexTypes("Customer.ShippingAddress#Address"),
- () => AssertUpdate(
- async,
- ss => ss.Set().Select(c => c.ShippingAddress).OrderBy(a => a.ZipCode).Skip(1),
- a => a,
- s => s.SetProperty(c => c.ZipCode, 12345),
- rowsAffectedCount: 3));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_complex_type_to_parameter(bool async)
@@ -222,7 +211,4 @@ public virtual Task Update_collection_inside_complex_type(bool async)
c => c,
s => s.SetProperty(x => x.ShippingAddress.Tags, new List { "new_tag1", "new_tag2" }),
rowsAffectedCount: 3);
-
- private void ClearLog()
- => Fixture.TestSqlLoggerFactory.Clear();
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
similarity index 85%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
index 4f9f3b323a7..9a753835de7 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
@@ -87,16 +87,6 @@ public virtual Task Delete_GroupBy_Where_Select_First_3(bool async)
.Where(g => g.Count() < 3).Select(g => g.First()).Any(i => i == e)),
rowsAffectedCount: 1);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteDelete", "EagleQuery"),
- () => AssertDelete(
- async,
- ss => ss.Set().Where(e => e.CountryId > 0),
- rowsAffectedCount: 1));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_base_type(bool async)
@@ -180,18 +170,4 @@ public virtual Task Update_where_using_hierarchy_derived(bool async)
e => e,
s => s.SetProperty(e => e.Name, "Monovia"),
rowsAffectedCount: 1);
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteUpdate", "EagleQuery"),
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(e => e.CountryId > 0),
- e => e,
- s => s.SetProperty(e => e.Name, "Eagle"),
- rowsAffectedCount: 1));
-
- protected abstract void ClearLog();
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/IBulkUpdatesFixtureBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/IBulkUpdatesFixtureBase.cs
similarity index 100%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/IBulkUpdatesFixtureBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/IBulkUpdatesFixtureBase.cs
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
similarity index 83%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
index 24f40dbdc70..f8841a46d87 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
@@ -15,6 +15,5 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
CoreEventId.MappedPropertyIgnoredWarning,
CoreEventId.MappedNavigationIgnoredWarning));
- public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
- => facade.UseTransaction(transaction.GetDbTransaction());
+ public abstract void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction);
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
similarity index 84%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
index 5682f4c5a09..195b23bd23e 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
@@ -7,14 +7,9 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class InheritanceBulkUpdatesTestBase : BulkUpdatesTestBase
+public abstract class InheritanceBulkUpdatesTestBase(TFixture fixture) : BulkUpdatesTestBase(fixture)
where TFixture : InheritanceBulkUpdatesFixtureBase, new()
{
- protected InheritanceBulkUpdatesTestBase(TFixture fixture)
- : base(fixture)
- {
- }
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_where_hierarchy(bool async)
@@ -87,16 +82,6 @@ public virtual Task Delete_GroupBy_Where_Select_First_3(bool async)
.Where(g => g.Count() < 3).Select(g => g.First()).Any(i => i == e)),
rowsAffectedCount: 2);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteDelete", "EagleQuery"),
- () => AssertDelete(
- async,
- ss => ss.Set().Where(e => e.CountryId > 0),
- rowsAffectedCount: 1));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_base_type(bool async)
@@ -181,18 +166,6 @@ public virtual Task Update_where_using_hierarchy_derived(bool async)
s => s.SetProperty(e => e.Name, "Monovia"),
rowsAffectedCount: 1);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_where_keyless_entity_mapped_to_sql_query(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator("ExecuteUpdate", "EagleQuery"),
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(e => e.CountryId > 0),
- e => e,
- s => s.SetProperty(e => e.Name, "Eagle"),
- rowsAffectedCount: 1));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_with_interface_in_property_expression(bool async)
@@ -212,7 +185,4 @@ public virtual Task Update_with_interface_in_EF_Property_in_property_expression(
e => e,
// ReSharper disable once RedundantCast
s => s.SetProperty(c => EF.Property((ISugary)c, nameof(ISugary.SugarGrams)), 0),
- rowsAffectedCount: 1);
-
- protected abstract void ClearLog();
-}
+ rowsAffectedCount: 1);}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
similarity index 77%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
index 3863718e20d..c5cca8e6334 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/NonSharedModelBulkUpdatesTestBase.cs
@@ -42,24 +42,6 @@ await AssertDelete(
context => context.Set(), rowsAffectedCount: 0);
}
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual async Task Delete_aggregate_root_when_table_sharing_with_non_owned_throws(bool async)
- {
- var contextFactory = await InitializeAsync(
- onModelCreating: mb =>
- {
- mb.Entity().HasOne().WithOne().HasForeignKey(e => e.Id);
- mb.Entity().ToTable(nameof(Owner));
- });
-
- await AssertTranslationFailedWithDetails(
- () => AssertDelete(
- async, contextFactory.CreateContext,
- context => context.Set(), rowsAffectedCount: 0),
- RelationalStrings.ExecuteDeleteOnTableSplitting(nameof(Owner)));
- }
-
[ConditionalTheory] // #33937, #33946
[MemberData(nameof(IsAsyncData))]
public virtual async Task Replace_ColumnExpression_in_column_setter(bool async)
@@ -186,62 +168,6 @@ await AssertUpdate(
rowsAffectedCount: 0);
}
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual async Task Update_main_table_in_entity_with_entity_splitting(bool async)
- {
- var contextFactory = await InitializeAsync(
- onModelCreating: mb => mb.Entity()
- .ToTable("Blogs")
- .SplitToTable(
- "BlogsPart1", tb =>
- {
- tb.Property(b => b.Title);
- tb.Property(b => b.Rating);
- }),
- seed: async context =>
- {
- context.Set().Add(new Blog { Title = "SomeBlog" });
- await context.SaveChangesAsync();
- });
-
- await AssertUpdate(
- async,
- contextFactory.CreateContext,
- ss => ss.Set(),
- s => s.SetProperty(b => b.CreationTimestamp, b => new DateTime(2020, 1, 1)),
- rowsAffectedCount: 1);
- }
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual async Task Update_non_main_table_in_entity_with_entity_splitting(bool async)
- {
- var contextFactory = await InitializeAsync(
- onModelCreating: mb => mb.Entity()
- .ToTable("Blogs")
- .SplitToTable(
- "BlogsPart1", tb =>
- {
- tb.Property(b => b.Title);
- tb.Property(b => b.Rating);
- }),
- seed: async context =>
- {
- context.Set().Add(new Blog { Title = "SomeBlog" });
- await context.SaveChangesAsync();
- });
-
- await AssertUpdate(
- async,
- contextFactory.CreateContext,
- ss => ss.Set(),
- s => s
- .SetProperty(b => b.Title, b => b.Rating.ToString())
- .SetProperty(b => b.Rating, b => b.Title!.Length),
- rowsAffectedCount: 1);
- }
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Delete_entity_with_auto_include(bool async)
@@ -414,20 +340,7 @@ public Task AssertUpdate(
Assert.Equal(rowsAffectedCount, result);
});
- protected static async Task AssertTranslationFailedWithDetails(Func query, string details)
- => Assert.Contains(
- RelationalStrings.NonQueryTranslationFailedWithDetails("", details)[21..],
- (await Assert.ThrowsAsync(query))
- .Message);
-
- public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
- => facade.UseTransaction(transaction.GetDbTransaction());
-
- protected TestSqlLoggerFactory TestSqlLoggerFactory
- => (TestSqlLoggerFactory)ListLoggerFactory;
-
- protected void ClearLog()
- => TestSqlLoggerFactory.Clear();
+ public abstract void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction);
#endregion
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs
similarity index 67%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs
index f6bd8eba4ae..238f0ec5917 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesFixture.cs
@@ -5,13 +5,12 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class NorthwindBulkUpdatesFixture : NorthwindQueryRelationalFixture,
+public abstract class NorthwindBulkUpdatesFixture : NorthwindQueryFixtureBase,
IBulkUpdatesFixtureBase
where TModelCustomizer : ITestModelCustomizer, new()
{
protected override string StoreName
=> "BulkUpdatesNorthwind";
- public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
- => facade.UseTransaction(transaction.GetDbTransaction());
+ public abstract void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction);
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
similarity index 87%
rename from test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
rename to test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
index b013d393c5c..50750b0913b 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
+++ b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
@@ -7,15 +7,88 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public abstract class NorthwindBulkUpdatesTestBase : BulkUpdatesTestBase
+public abstract class NorthwindBulkUpdatesTestBase(TFixture fixture) : BulkUpdatesTestBase(fixture)
where TFixture : NorthwindBulkUpdatesFixture, new()
{
- protected NorthwindBulkUpdatesTestBase(TFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_non_entity_projection(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250).Select(e => e.ProductID),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_non_entity_projection_2(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250)
+ .Select(e => new OrderDetail { OrderID = e.OrderID, ProductID = e.ProductID }),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_non_entity_projection_3(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250)
+ .Select(e => new { OrderDetail = e, e.ProductID }),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_without_property_to_set_throws(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250),
+ e => e,
+ s => s,
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_with_invalid_lambda_throws(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250),
+ e => e,
+ s => s.Maybe(e => e),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_with_invalid_lambda_in_set_property_throws(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(od => od.OrderID < 10250),
+ e => e,
+ s => s.SetProperty(e => e.MaybeScalar(e => e.OrderID), 10300),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_multiple_tables_throws(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set()
+ .Where(o => o.CustomerID.StartsWith("F"))
+ .Select(e => new { e, e.Customer }),
+ e => e.Customer,
+ s => s
+ .SetProperty(c => c.Customer.ContactName, "Name")
+ .SetProperty(c => c.e.OrderDate, new DateTime(2020, 1, 1)),
+ rowsAffectedCount: 0);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_unmapped_property_throws(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")),
+ e => e,
+ s => s.SetProperty(c => c.IsLondon, true),
+ rowsAffectedCount: 0);
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
@@ -242,62 +315,6 @@ public virtual Task Delete_Except(bool async)
.Except(ss.Set().Where(od => od.OrderID > 11250)),
rowsAffectedCount: 5);
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_non_entity_projection(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteDeleteOnNonEntityType,
- () => AssertDelete(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250).Select(e => e.ProductID),
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_non_entity_projection_2(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteDeleteOnNonEntityType,
- () => AssertDelete(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250)
- .Select(e => new OrderDetail { OrderID = e.OrderID, ProductID = e.ProductID }),
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_non_entity_projection_3(bool async)
- => AssertTranslationFailed(
- RelationalStrings.ExecuteDeleteOnNonEntityType,
- () => AssertDelete(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250)
- .Select(e => new { OrderDetail = e, e.ProductID }),
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Delete_FromSql_converted_to_subquery(bool async)
- => TestHelpers.ExecuteWithStrategyInTransactionAsync(
- () => Fixture.CreateContext(),
- (facade, transaction) => Fixture.UseTransaction(facade, transaction),
- async context =>
- {
- var queryable = context.Set().FromSqlRaw(
- NormalizeDelimitersInRawString(
- @"SELECT [OrderID], [ProductID], [UnitPrice], [Quantity], [Discount]
-FROM [Order Details]
-WHERE [OrderID] < 10300"));
-
- if (async)
- {
- await queryable.ExecuteDeleteAsync();
- }
- else
- {
- queryable.ExecuteDelete();
- }
- });
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_Where_optional_navigation_predicate(bool async)
@@ -719,30 +736,6 @@ public virtual Task Update_Where_set_null(bool async)
rowsAffectedCount: 8,
(b, a) => Assert.All(a, c => Assert.Null(c.ContactName)));
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_without_property_to_set_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.NoSetPropertyInvocation,
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250),
- e => e,
- s => s,
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_with_invalid_lambda_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.InvalidArgumentToExecuteUpdate,
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250),
- e => e,
- s => s.Maybe(e => e),
- rowsAffectedCount: 0));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Where_multiple_set(bool async)
@@ -762,47 +755,6 @@ public virtual Task Update_Where_multiple_set(bool async)
}));
}
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_with_invalid_lambda_in_set_property_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.InvalidPropertyInSetProperty(
- new ExpressionPrinter().PrintExpression((OrderDetail e) => e.MaybeScalar(e => e.OrderID))),
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(od => od.OrderID < 10250),
- e => e,
- s => s.SetProperty(e => e.MaybeScalar(e => e.OrderID), 10300),
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_multiple_tables_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.MultipleTablesInExecuteUpdate("c => c.Customer.ContactName", "c => c.e.OrderDate"),
- () => AssertUpdate(
- async,
- ss => ss.Set()
- .Where(o => o.CustomerID.StartsWith("F"))
- .Select(e => new { e, e.Customer }),
- e => e.Customer,
- s => s
- .SetProperty(c => c.Customer.ContactName, "Name")
- .SetProperty(c => c.e.OrderDate, new DateTime(2020, 1, 1)),
- rowsAffectedCount: 0));
-
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_unmapped_property_throws(bool async)
- => AssertTranslationFailed(
- RelationalStrings.InvalidPropertyInSetProperty("c => c.IsLondon"),
- () => AssertUpdate(
- async,
- ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")),
- e => e,
- s => s.SetProperty(c => c.IsLondon, true),
- rowsAffectedCount: 0));
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Union_set_constant(bool async)
@@ -973,30 +925,6 @@ from o in ss.Set().Where(o => o.OrderID < 10300 && o.OrderDate.Value.Year
rowsAffectedCount: 8,
(b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
- [ConditionalTheory]
- [MemberData(nameof(IsAsyncData))]
- public virtual Task Update_FromSql_set_constant(bool async)
- => TestHelpers.ExecuteWithStrategyInTransactionAsync(
- () => Fixture.CreateContext(),
- (facade, transaction) => Fixture.UseTransaction(facade, transaction),
- async context =>
- {
- var queryable = context.Set().FromSqlRaw(
- NormalizeDelimitersInRawString(
- @"SELECT [Region], [PostalCode], [Phone], [Fax], [CustomerID], [Country], [ContactTitle], [ContactName], [CompanyName], [City], [Address]
-FROM [Customers]
-WHERE [CustomerID] LIKE 'A%'"));
-
- if (async)
- {
- await queryable.ExecuteUpdateAsync(s => s.SetProperty(c => c.ContactName, "Updated"));
- }
- else
- {
- queryable.ExecuteUpdate(s => s.SetProperty(c => c.ContactName, "Updated"));
- }
- });
-
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Where_SelectMany_subquery_set_null(bool async)
@@ -1058,10 +986,4 @@ public virtual Task Update_with_two_inner_joins(bool async)
s => s.SetProperty(od => od.Quantity, 1),
rowsAffectedCount: 228,
(b, a) => Assert.All(a, od => Assert.Equal(1, od.Quantity)));
-
- protected string NormalizeDelimitersInRawString(string sql)
- => Fixture.TestStore.NormalizeDelimitersInRawString(sql);
-
- protected virtual void ClearLog()
- => Fixture.TestSqlLoggerFactory.Clear();
}
diff --git a/test/EFCore.Specification.Tests/TestUtilities/BulkUpdatesAsserter.cs b/test/EFCore.Specification.Tests/TestUtilities/BulkUpdatesAsserter.cs
new file mode 100644
index 00000000000..c03cecd9125
--- /dev/null
+++ b/test/EFCore.Specification.Tests/TestUtilities/BulkUpdatesAsserter.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable disable
+using Microsoft.EntityFrameworkCore.BulkUpdates;
+
+namespace Microsoft.EntityFrameworkCore.TestUtilities;
+
+public class BulkUpdatesAsserter(IBulkUpdatesFixtureBase queryFixture, Func rewriteServerQueryExpression)
+{
+ private readonly Func _contextCreator = queryFixture.GetContextCreator();
+ private readonly Action _useTransaction = queryFixture.GetUseTransaction();
+ private readonly Func _setSourceCreator = queryFixture.GetSetSourceCreator();
+ private readonly Func _rewriteServerQueryExpression = rewriteServerQueryExpression;
+ private readonly IReadOnlyDictionary _entitySorters = queryFixture.EntitySorters ?? new Dictionary();
+
+ public Task AssertDelete(
+ bool async,
+ Func> query,
+ int rowsAffectedCount)
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ _contextCreator, _useTransaction,
+ async context =>
+ {
+ var processedQuery = RewriteServerQuery(query(_setSourceCreator(context)));
+
+ var result = async
+ ? await processedQuery.ExecuteDeleteAsync()
+ : processedQuery.ExecuteDelete();
+
+ Assert.Equal(rowsAffectedCount, result);
+ });
+
+ public Task AssertUpdate(
+ bool async,
+ Func> query,
+ Expression> entitySelector,
+ Expression, SetPropertyCalls>> setPropertyCalls,
+ int rowsAffectedCount,
+ Action, IReadOnlyList> asserter)
+ where TResult : class
+ => TestHelpers.ExecuteWithStrategyInTransactionAsync(
+ _contextCreator, _useTransaction,
+ async context =>
+ {
+ var elementSorter = (Func)_entitySorters[typeof(TEntity)];
+
+ var processedQuery = RewriteServerQuery(query(_setSourceCreator(context)));
+
+ var before = processedQuery.AsNoTracking().Select(entitySelector).OrderBy(elementSorter).ToList();
+
+ var result = async
+ ? await processedQuery.ExecuteUpdateAsync(setPropertyCalls)
+ : processedQuery.ExecuteUpdate(setPropertyCalls);
+
+ Assert.Equal(rowsAffectedCount, result);
+
+ var after = processedQuery.AsNoTracking().Select(entitySelector).OrderBy(elementSorter).ToList();
+
+ asserter?.Invoke(before, after);
+ });
+
+ private IQueryable RewriteServerQuery(IQueryable query)
+ => query.Provider.CreateQuery(_rewriteServerQueryExpression(query.Expression));
+}
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
index 4ad3392a469..1c719aa04e4 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
@@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class ComplexTypeBulkUpdatesSqlServerTest(ComplexTypeBulkUpdatesSqlServerTest.ComplexTypeBulkUpdatesSqlServerFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTypeBulkUpdatesTestBase<
+public class ComplexTypeBulkUpdatesSqlServerTest(ComplexTypeBulkUpdatesSqlServerTest.ComplexTypeBulkUpdatesSqlServerFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTypeBulkUpdatesRelationalTestBase<
ComplexTypeBulkUpdatesSqlServerTest.ComplexTypeBulkUpdatesSqlServerFixture>(fixture, testOutputHelper)
{
public override async Task Delete_entity_type_with_complex_type(bool async)
@@ -20,9 +20,9 @@ FROM [Customer] AS [c]
""");
}
- public override async Task Delete_complex_type_throws(bool async)
+ public override async Task Delete_complex_type(bool async)
{
- await base.Delete_complex_type_throws(async);
+ await base.Delete_complex_type(async);
AssertSql();
}
@@ -93,9 +93,9 @@ FROM [Customer] AS [c]
""");
}
- public override async Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool async)
+ public override async Task Update_projected_complex_type_via_OrderBy_Skip(bool async)
{
- await base.Update_projected_complex_type_via_OrderBy_Skip_throws(async);
+ await base.Update_projected_complex_type_via_OrderBy_Skip(async);
AssertExecuteUpdateSql();
}
@@ -241,7 +241,7 @@ private void AssertSql(params string[] expected)
protected void ClearLog()
=> Fixture.TestSqlLoggerFactory.Clear();
- public class ComplexTypeBulkUpdatesSqlServerFixture : ComplexTypeBulkUpdatesFixtureBase
+ public class ComplexTypeBulkUpdatesSqlServerFixture : ComplexTypeBulkUpdatesRelationalFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqlServerTestStoreFactory.Instance;
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
index e973a55ab36..81b06f363b9 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqlServerTest.cs
@@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class NonSharedModelBulkUpdatesSqlServerTest : NonSharedModelBulkUpdatesTestBase
+public class NonSharedModelBulkUpdatesSqlServerTest : NonSharedModelBulkUpdatesRelationalTestBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqlServerTestStoreFactory.Instance;
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerFixture.cs
index 0bec7c67a22..f59376bb3c9 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerFixture.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerFixture.cs
@@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class NorthwindBulkUpdatesSqlServerFixture : NorthwindBulkUpdatesFixture
+public class NorthwindBulkUpdatesSqlServerFixture : NorthwindBulkUpdatesRelationalFixture
where TModelCustomizer : ITestModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
index dc962065b0c..27dd7719563 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
@@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
public class NorthwindBulkUpdatesSqlServerTest(
NorthwindBulkUpdatesSqlServerFixture fixture,
- ITestOutputHelper testOutputHelper) : NorthwindBulkUpdatesTestBase>(fixture, testOutputHelper)
+ ITestOutputHelper testOutputHelper) : NorthwindBulkUpdatesRelationalTestBase>(fixture, testOutputHelper)
{
[ConditionalFact]
public virtual void Check_all_tests_overridden()
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqlServerTest.cs
index 49231367e12..1972ddd3415 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqlServerTest.cs
@@ -5,18 +5,12 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class TPHFiltersInheritanceBulkUpdatesSqlServerTest : FiltersInheritanceBulkUpdatesTestBase<
- TPHFiltersInheritanceBulkUpdatesSqlServerFixture>
+public class TPHFiltersInheritanceBulkUpdatesSqlServerTest(
+ TPHFiltersInheritanceBulkUpdatesSqlServerFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : FiltersInheritanceBulkUpdatesRelationalTestBase<
+ TPHFiltersInheritanceBulkUpdatesSqlServerFixture>(fixture, testOutputHelper)
{
- public TPHFiltersInheritanceBulkUpdatesSqlServerTest(
- TPHFiltersInheritanceBulkUpdatesSqlServerFixture fixture,
- ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqlServerTest.cs
index 15cd8e98c32..abf70551a6b 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqlServerTest.cs
@@ -5,17 +5,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class TPHInheritanceBulkUpdatesSqlServerTest : TPHInheritanceBulkUpdatesTestBase
+public class TPHInheritanceBulkUpdatesSqlServerTest(
+ TPHInheritanceBulkUpdatesSqlServerFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : TPHInheritanceBulkUpdatesTestBase(fixture, testOutputHelper)
{
- public TPHInheritanceBulkUpdatesSqlServerTest(
- TPHInheritanceBulkUpdatesSqlServerFixture fixture,
- ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqliteTest.cs
index 62699586106..8b6bccba5b5 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqliteTest.cs
@@ -5,10 +5,10 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class ComplexTypeBulkUpdatesSqliteTest(ComplexTypeBulkUpdatesSqliteTest.ComplexTypeBulkUpdatesSqliteFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTypeBulkUpdatesTestBase<
+public class ComplexTypeBulkUpdatesSqliteTest(ComplexTypeBulkUpdatesSqliteTest.ComplexTypeBulkUpdatesSqliteFixture fixture, ITestOutputHelper testOutputHelper) : ComplexTypeBulkUpdatesRelationalTestBase<
ComplexTypeBulkUpdatesSqliteTest.ComplexTypeBulkUpdatesSqliteFixture>(fixture, testOutputHelper)
{
- public class ComplexTypeBulkUpdatesSqliteFixture : ComplexTypeBulkUpdatesFixtureBase
+ public class ComplexTypeBulkUpdatesSqliteFixture : ComplexTypeBulkUpdatesRelationalFixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqliteTestStoreFactory.Instance;
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
index 6809d2912e7..52763b754ae 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NonSharedModelBulkUpdatesSqliteTest.cs
@@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class NonSharedModelBulkUpdatesSqliteTest : NonSharedModelBulkUpdatesTestBase
+public class NonSharedModelBulkUpdatesSqliteTest : NonSharedModelBulkUpdatesRelationalTestBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqliteTestStoreFactory.Instance;
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteFixture.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteFixture.cs
index 2ddb4cc6efa..9c63144fe5f 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteFixture.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteFixture.cs
@@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class NorthwindBulkUpdatesSqliteFixture : NorthwindBulkUpdatesFixture
+public class NorthwindBulkUpdatesSqliteFixture : NorthwindBulkUpdatesRelationalFixture
where TModelCustomizer : ITestModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
index 39516e4e8b4..b9d24ca7352 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
@@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
public class NorthwindBulkUpdatesSqliteTest(
NorthwindBulkUpdatesSqliteFixture fixture,
- ITestOutputHelper testOutputHelper) : NorthwindBulkUpdatesTestBase>(fixture, testOutputHelper)
+ ITestOutputHelper testOutputHelper) : NorthwindBulkUpdatesRelationalTestBase>(fixture, testOutputHelper)
{
[ConditionalFact]
public virtual void Check_all_tests_overridden()
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqliteTest.cs
index 9ab20e3d02d..0546ac8b6ea 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHFiltersInheritanceBulkUpdatesSqliteTest.cs
@@ -5,18 +5,12 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class TPHFiltersInheritanceBulkUpdatesSqliteTest : FiltersInheritanceBulkUpdatesTestBase<
- TPHFiltersInheritanceBulkUpdatesSqliteFixture>
+public class TPHFiltersInheritanceBulkUpdatesSqliteTest(
+ TPHFiltersInheritanceBulkUpdatesSqliteFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : FiltersInheritanceBulkUpdatesRelationalTestBase<
+ TPHFiltersInheritanceBulkUpdatesSqliteFixture>(fixture, testOutputHelper)
{
- public TPHFiltersInheritanceBulkUpdatesSqliteTest(
- TPHFiltersInheritanceBulkUpdatesSqliteFixture fixture,
- ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqliteTest.cs
index b712ab1119f..ceb88407b32 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPHInheritanceBulkUpdatesSqliteTest.cs
@@ -5,17 +5,11 @@ namespace Microsoft.EntityFrameworkCore.BulkUpdates;
#nullable disable
-public class TPHInheritanceBulkUpdatesSqliteTest : TPHInheritanceBulkUpdatesTestBase
+public class TPHInheritanceBulkUpdatesSqliteTest(
+ TPHInheritanceBulkUpdatesSqliteFixture fixture,
+ ITestOutputHelper testOutputHelper)
+ : TPHInheritanceBulkUpdatesTestBase(fixture, testOutputHelper)
{
- public TPHInheritanceBulkUpdatesSqliteTest(
- TPHInheritanceBulkUpdatesSqliteFixture fixture,
- ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
diff --git a/test/EFCore.Tests/Query/QueryProviderTest.cs b/test/EFCore.Tests/Query/QueryProviderTest.cs
index 082ad4bf039..44de09533fe 100644
--- a/test/EFCore.Tests/Query/QueryProviderTest.cs
+++ b/test/EFCore.Tests/Query/QueryProviderTest.cs
@@ -5,6 +5,29 @@ namespace Microsoft.EntityFrameworkCore.Query;
public class QueryProviderTest
{
+ [ConditionalFact]
+ public async Task ExecuteUpdate_and_ExecuteDelete_throw_when_provider_does_not_implement()
+ {
+ using var context = new TestContext();
+ var set = context.TestEntities;
+
+ Assert.Equal(
+ CoreStrings.ExecuteQueriesNotSupported("ExecuteUpdate", "ExecuteUpdateAsync"),
+ Assert.Throws(() => set.ExecuteUpdate(s => s.SetProperty(e => e.Id, 1))).Message);
+
+ Assert.Equal(
+ CoreStrings.ExecuteQueriesNotSupported("ExecuteUpdate", "ExecuteUpdateAsync"),
+ (await Assert.ThrowsAsync(() => set.ExecuteUpdateAsync(s => s.SetProperty(e => e.Id, 1)))).Message);
+
+ Assert.Equal(
+ CoreStrings.ExecuteQueriesNotSupported("ExecuteDelete", "ExecuteDeleteAsync"),
+ Assert.Throws(() => set.ExecuteDelete()).Message);
+
+ Assert.Equal(
+ CoreStrings.ExecuteQueriesNotSupported("ExecuteDelete", "ExecuteDeleteAsync"),
+ (await Assert.ThrowsAsync(() => set.ExecuteDeleteAsync())).Message);
+ }
+
[ConditionalFact]
public void Non_generic_ExecuteQuery_does_not_throw()
{