diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
index f32420c4660..1cbec0576f6 100644
--- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs
+++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
@@ -83,7 +83,7 @@ protected virtual void GenerateRootCommand(Expression queryExpression)
switch (queryExpression)
{
case SelectExpression selectExpression:
- GenerateTagsHeaderComment(selectExpression);
+ GenerateTagsHeaderComment(selectExpression.Tags);
if (selectExpression.IsNonComposedFromSql())
{
@@ -95,6 +95,16 @@ protected virtual void GenerateRootCommand(Expression queryExpression)
}
break;
+ case UpdateExpression updateExpression:
+ GenerateTagsHeaderComment(updateExpression.Tags);
+ VisitUpdate(updateExpression);
+ break;
+
+ case DeleteExpression deleteExpression:
+ GenerateTagsHeaderComment(deleteExpression.Tags);
+ VisitDelete(deleteExpression);
+ break;
+
default:
base.Visit(queryExpression);
break;
@@ -117,6 +127,7 @@ protected virtual IRelationalCommandBuilder Sql
/// Generates the head comment for tags.
///
/// A select expression to generate tags for.
+ [Obsolete("Use the method which takes tags instead.")]
protected virtual void GenerateTagsHeaderComment(SelectExpression selectExpression)
{
if (selectExpression.Tags.Count > 0)
@@ -130,6 +141,23 @@ protected virtual void GenerateTagsHeaderComment(SelectExpression selectExpressi
}
}
+ ///
+ /// Generates the head comment for tags.
+ ///
+ /// A set of tags to print as comment.
+ protected virtual void GenerateTagsHeaderComment(ISet tags)
+ {
+ if (tags.Count > 0)
+ {
+ foreach (var tag in tags)
+ {
+ _relationalCommandBuilder.AppendLines(_sqlGenerationHelper.GenerateComment(tag));
+ }
+
+ _relationalCommandBuilder.AppendLine();
+ }
+ }
+
///
protected override Expression VisitSqlFragment(SqlFragmentExpression sqlFragmentExpression)
{
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index 99afa6f307e..6ba42322788 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -1079,13 +1079,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
var clrType = entityType.ClrType;
var entityParameter = Expression.Parameter(clrType);
Expression predicateBody;
- //if (pk.Properties.Count == 1)
- //{
- // predicateBody = Expression.Call(
- // EnumerableMethods.Contains.MakeGenericMethod(clrType), source, entityParameter);
- //}
- //else
- //{
var innerParameter = Expression.Parameter(clrType);
predicateBody = Expression.Call(
QueryableMethods.AnyWithPredicate.MakeGenericMethod(clrType),
@@ -1093,7 +1086,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
Expression.Quote(Expression.Lambda(
Infrastructure.ExpressionExtensions.CreateEqualsExpression(innerParameter, entityParameter),
innerParameter)));
- //}
var newSource = Expression.Call(
QueryableMethods.Where.MakeGenericMethod(clrType),
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
index 733eab08943..103c641f8ae 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
@@ -56,11 +56,24 @@ protected override Expression VisitExtension(Expression extensionExpression)
/// An expression which executes a non-query operation.
protected virtual Expression VisitNonQuery(NonQueryExpression nonQueryExpression)
{
+ // Apply tags
+ var innerExpression = nonQueryExpression.Expression;
+ switch (innerExpression)
+ {
+ case UpdateExpression updateExpression:
+ innerExpression = updateExpression.ApplyTags(_tags);
+ break;
+
+ case DeleteExpression deleteExpression:
+ innerExpression = deleteExpression.ApplyTags(_tags);
+ break;
+ }
+
var relationalCommandCache = new RelationalCommandCache(
Dependencies.MemoryCache,
RelationalDependencies.QuerySqlGeneratorFactory,
RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
- nonQueryExpression.Expression,
+ innerExpression,
_useRelationalNulls);
return Expression.Call(
diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
index 83f40952443..a7e46feef10 100644
--- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
@@ -606,8 +605,82 @@ protected override Expression VisitExtension(Expression extensionExpression)
return new EntityReferenceExpression(entityShaperExpression);
case ProjectionBindingExpression projectionBindingExpression:
- return ((SelectExpression)projectionBindingExpression.QueryExpression)
- .GetProjection(projectionBindingExpression);
+ return Visit(((SelectExpression)projectionBindingExpression.QueryExpression)
+ .GetProjection(projectionBindingExpression));
+
+ case ShapedQueryExpression shapedQueryExpression:
+ if (shapedQueryExpression.ResultCardinality == ResultCardinality.Enumerable)
+ {
+ return QueryCompilationContext.NotTranslatedExpression;
+ }
+
+ var shaperExpression = shapedQueryExpression.ShaperExpression;
+ ProjectionBindingExpression? mappedProjectionBindingExpression = null;
+
+ var innerExpression = shaperExpression;
+ Type? convertedType = null;
+ if (shaperExpression is UnaryExpression unaryExpression
+ && unaryExpression.NodeType == ExpressionType.Convert)
+ {
+ convertedType = unaryExpression.Type;
+ innerExpression = unaryExpression.Operand;
+ }
+
+ if (innerExpression is EntityShaperExpression ese
+ && (convertedType == null
+ || convertedType.IsAssignableFrom(ese.Type)))
+ {
+ return new EntityReferenceExpression(shapedQueryExpression.UpdateShaperExpression(innerExpression));
+ }
+
+ if (innerExpression is ProjectionBindingExpression pbe
+ && (convertedType == null
+ || convertedType.MakeNullable() == innerExpression.Type))
+ {
+ mappedProjectionBindingExpression = pbe;
+ }
+
+ if (mappedProjectionBindingExpression == null
+ && shaperExpression is BlockExpression blockExpression
+ && blockExpression.Expressions.Count == 2
+ && blockExpression.Expressions[0] is BinaryExpression binaryExpression
+ && binaryExpression.NodeType == ExpressionType.Assign
+ && binaryExpression.Right is ProjectionBindingExpression pbe2)
+ {
+ mappedProjectionBindingExpression = pbe2;
+ }
+
+ if (mappedProjectionBindingExpression == null)
+ {
+ return QueryCompilationContext.NotTranslatedExpression;
+ }
+
+ var subquery = (SelectExpression)shapedQueryExpression.QueryExpression;
+ var projection = subquery.GetProjection(mappedProjectionBindingExpression);
+ if (projection is not SqlExpression sqlExpression)
+ {
+ return QueryCompilationContext.NotTranslatedExpression;
+ }
+
+ if (subquery.Tables.Count == 0)
+ {
+ return sqlExpression;
+ }
+
+ subquery.ReplaceProjection(new List { sqlExpression });
+ subquery.ApplyProjection();
+
+ SqlExpression scalarSubqueryExpression = new ScalarSubqueryExpression(subquery);
+
+ if (shapedQueryExpression.ResultCardinality == ResultCardinality.SingleOrDefault
+ && !shaperExpression.Type.IsNullableType())
+ {
+ scalarSubqueryExpression = _sqlExpressionFactory.Coalesce(
+ scalarSubqueryExpression,
+ (SqlExpression)Visit(shaperExpression.Type.GetDefaultValueConstant()));
+ }
+
+ return scalarSubqueryExpression;
default:
return QueryCompilationContext.NotTranslatedExpression;
@@ -632,7 +705,7 @@ protected override Expression VisitMember(MemberExpression memberExpression)
var innerExpression = Visit(memberExpression.Expression);
return TryBindMember(innerExpression, MemberIdentity.Create(memberExpression.Member))
- ?? (TranslationFailed(memberExpression.Expression, Visit(memberExpression.Expression), out var sqlInnerExpression)
+ ?? (TranslationFailed(memberExpression.Expression, innerExpression, out var sqlInnerExpression)
? QueryCompilationContext.NotTranslatedExpression
: Dependencies.MemberTranslatorProvider.Translate(
sqlInnerExpression, memberExpression.Member, memberExpression.Type, _queryCompilationContext.Logger))
@@ -662,6 +735,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
}
+ // EF.Default
+ if (methodCallExpression.Method.IsEFDefaultMethod())
+ {
+ return new SqlFragmentExpression("DEFAULT");
+ }
+
var method = methodCallExpression.Method;
var arguments = methodCallExpression.Arguments;
EnumerableExpression? enumerableExpression = null;
@@ -792,9 +871,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
: method;
var enumerableSource = Visit(arguments[0]);
- if (enumerableSource is EnumerableExpression)
+ if (enumerableSource is EnumerableExpression ee)
{
- enumerableExpression = (EnumerableExpression)enumerableSource;
+ enumerableExpression = ee;
switch (method.Name)
{
case nameof(Queryable.AsQueryable)
@@ -928,10 +1007,10 @@ when QueryableMethods.IsSumWithSelector(genericMethod):
&& !skipVisitChildren)
{
var @object = Visit(methodCallExpression.Object);
- if (@object is EnumerableExpression)
+ if (@object is EnumerableExpression eeo)
{
// This is safe since if enumerableExpression is non-null then it was static method
- enumerableExpression = (EnumerableExpression)@object;
+ enumerableExpression = eeo;
}
else if (TranslationFailed(methodCallExpression.Object, @object, out sqlObject))
{
@@ -944,7 +1023,7 @@ when QueryableMethods.IsSumWithSelector(genericMethod):
{
var argument = arguments[i];
var visitedArgument = Visit(argument);
- if (visitedArgument is EnumerableExpression)
+ if (visitedArgument is EnumerableExpression eea)
{
if (enumerableExpression != null)
{
@@ -952,7 +1031,7 @@ when QueryableMethods.IsSumWithSelector(genericMethod):
break;
}
- enumerableExpression = (EnumerableExpression)visitedArgument;
+ enumerableExpression = eea;
continue;
}
@@ -1009,83 +1088,10 @@ when QueryableMethods.IsSumWithSelector(genericMethod):
// Subquery case
var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);
- if (subqueryTranslation != null)
- {
- if (subqueryTranslation.ResultCardinality == ResultCardinality.Enumerable)
- {
- return QueryCompilationContext.NotTranslatedExpression;
- }
-
- var shaperExpression = subqueryTranslation.ShaperExpression;
- ProjectionBindingExpression? mappedProjectionBindingExpression = null;
-
- var innerExpression = shaperExpression;
- Type? convertedType = null;
- if (shaperExpression is UnaryExpression unaryExpression
- && unaryExpression.NodeType == ExpressionType.Convert)
- {
- convertedType = unaryExpression.Type;
- innerExpression = unaryExpression.Operand;
- }
- if (innerExpression is EntityShaperExpression ese
- && (convertedType == null
- || convertedType.IsAssignableFrom(ese.Type)))
- {
- return new EntityReferenceExpression(subqueryTranslation.UpdateShaperExpression(innerExpression));
- }
-
- if (innerExpression is ProjectionBindingExpression pbe
- && (convertedType == null
- || convertedType.MakeNullable() == innerExpression.Type))
- {
- mappedProjectionBindingExpression = pbe;
- }
-
- if (mappedProjectionBindingExpression == null
- && shaperExpression is BlockExpression blockExpression
- && blockExpression.Expressions.Count == 2
- && blockExpression.Expressions[0] is BinaryExpression binaryExpression
- && binaryExpression.NodeType == ExpressionType.Assign
- && binaryExpression.Right is ProjectionBindingExpression pbe2)
- {
- mappedProjectionBindingExpression = pbe2;
- }
-
- if (mappedProjectionBindingExpression == null)
- {
- return QueryCompilationContext.NotTranslatedExpression;
- }
-
- var subquery = (SelectExpression)subqueryTranslation.QueryExpression;
- var projection = subquery.GetProjection(mappedProjectionBindingExpression);
- if (projection is not SqlExpression sqlExpression)
- {
- return QueryCompilationContext.NotTranslatedExpression;
- }
-
- if (subquery.Tables.Count == 0)
- {
- return sqlExpression;
- }
-
- subquery.ReplaceProjection(new List { sqlExpression });
- subquery.ApplyProjection();
-
- SqlExpression scalarSubqueryExpression = new ScalarSubqueryExpression(subquery);
-
- if (subqueryTranslation.ResultCardinality == ResultCardinality.SingleOrDefault
- && !shaperExpression.Type.IsNullableType())
- {
- scalarSubqueryExpression = _sqlExpressionFactory.Coalesce(
- scalarSubqueryExpression,
- (SqlExpression)Visit(shaperExpression.Type.GetDefaultValueConstant()));
- }
-
- return scalarSubqueryExpression;
- }
-
- return QueryCompilationContext.NotTranslatedExpression;
+ return subqueryTranslation == null
+ ? QueryCompilationContext.NotTranslatedExpression
+ : Visit(subqueryTranslation);
}
///
@@ -1394,12 +1400,7 @@ private static EnumerableExpression ProcessSelector(EnumerableExpression enumera
{
var lambdaBody = RemapLambda(enumerableExpression, lambdaExpression);
var predicate = TranslateInternal(lambdaBody);
- if (predicate == null)
- {
- return null;
- }
-
- return enumerableExpression.ApplyPredicate(predicate);
+ return predicate == null ? null : enumerableExpression.ApplyPredicate(predicate);
}
private static Expression TryRemoveImplicitConvert(Expression expression)
diff --git a/src/EFCore.Relational/Query/SqlExpressions/DeleteExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/DeleteExpression.cs
index 1af829df28e..b46b336fd08 100644
--- a/src/EFCore.Relational/Query/SqlExpressions/DeleteExpression.cs
+++ b/src/EFCore.Relational/Query/SqlExpressions/DeleteExpression.cs
@@ -19,11 +19,22 @@ public sealed class DeleteExpression : Expression, IPrintableExpression
/// A table on which the delete operation is being applied.
/// A select expression which is used to determine which rows to delete.
public DeleteExpression(TableExpression table, SelectExpression selectExpression)
+ : this(table, selectExpression, new HashSet())
+ {
+ }
+
+ private DeleteExpression(TableExpression table, SelectExpression selectExpression, ISet tags)
{
Table = table;
SelectExpression = selectExpression;
+ Tags = tags;
}
+ ///
+ /// The list of tags applied to this .
+ ///
+ public ISet Tags { get; }
+
///
/// The table on which the delete operation is being applied.
///
@@ -34,6 +45,13 @@ public DeleteExpression(TableExpression table, SelectExpression selectExpression
///
public SelectExpression SelectExpression { get; }
+ ///
+ /// Applies a given set of tags.
+ ///
+ /// A list of tags to apply.
+ public DeleteExpression ApplyTags(ISet tags)
+ => new(Table, SelectExpression, tags);
+
///
public override Type Type
=> typeof(object);
@@ -58,12 +76,17 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
/// This expression if no children changed, or an expression with the updated children.
public DeleteExpression Update(SelectExpression selectExpression)
=> selectExpression != SelectExpression
- ? new DeleteExpression(Table, selectExpression)
+ ? new DeleteExpression(Table, selectExpression, Tags)
: this;
///
public void Print(ExpressionPrinter expressionPrinter)
{
+ foreach (var tag in Tags)
+ {
+ expressionPrinter.Append($"-- {tag}");
+ }
+ expressionPrinter.AppendLine();
expressionPrinter.AppendLine($"DELETE FROM {Table.Name} AS {Table.Alias}");
expressionPrinter.Visit(SelectExpression);
}
diff --git a/src/EFCore.Relational/Query/SqlExpressions/UpdateExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/UpdateExpression.cs
index 7a41ae30dca..2260a28aa72 100644
--- a/src/EFCore.Relational/Query/SqlExpressions/UpdateExpression.cs
+++ b/src/EFCore.Relational/Query/SqlExpressions/UpdateExpression.cs
@@ -21,12 +21,24 @@ public sealed class UpdateExpression : Expression, IPrintableExpression
/// A select expression which is used to determine which rows to update and to get data from additional tables.
/// A list of which specifies columns and their corresponding values to update.
public UpdateExpression(TableExpression table, SelectExpression selectExpression, IReadOnlyList setColumnValues)
+ : this(table, selectExpression, setColumnValues, new HashSet())
+ {
+ }
+
+ private UpdateExpression(
+ TableExpression table, SelectExpression selectExpression, IReadOnlyList setColumnValues, ISet tags)
{
Table = table;
SelectExpression = selectExpression;
SetColumnValues = setColumnValues;
+ Tags = tags;
}
+ ///
+ /// The list of tags applied to this .
+ ///
+ public ISet Tags { get; }
+
///
/// The table on which the update operation is being applied.
///
@@ -42,6 +54,13 @@ public UpdateExpression(TableExpression table, SelectExpression selectExpression
///
public IReadOnlyList SetColumnValues { get; }
+ ///
+ /// Applies a given set of tags.
+ ///
+ /// A list of tags to apply.
+ public UpdateExpression ApplyTags(ISet tags)
+ => new(Table, SelectExpression, SetColumnValues, tags);
+
///
public override Type Type
=> typeof(object);
@@ -89,12 +108,17 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
/// This expression if no children changed, or an expression with the updated children.
public UpdateExpression Update(SelectExpression selectExpression, IReadOnlyList setColumnValues)
=> selectExpression != SelectExpression || !SetColumnValues.SequenceEqual(setColumnValues)
- ? new UpdateExpression(Table, selectExpression, setColumnValues)
+ ? new UpdateExpression(Table, selectExpression, setColumnValues, Tags)
: this;
///
public void Print(ExpressionPrinter expressionPrinter)
{
+ foreach (var tag in Tags)
+ {
+ expressionPrinter.Append($"-- {tag}");
+ }
+ expressionPrinter.AppendLine();
expressionPrinter.AppendLine($"UPDATE {Table.Name} AS {Table.Alias}");
expressionPrinter.AppendLine("SET");
using (expressionPrinter.Indent())
diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
index 1eaf3487b90..8e51fc612da 100644
--- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
+++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
@@ -112,7 +112,7 @@ private UpdateExpression VisitUpdate(UpdateExpression updateExpression)
return selectExpression != updateExpression.SelectExpression
|| setColumnValues != null
- ? new UpdateExpression(updateExpression.Table, selectExpression, setColumnValues ?? updateExpression.SetColumnValues)
+ ? updateExpression.Update(selectExpression, setColumnValues ?? updateExpression.SetColumnValues)
: updateExpression;
}
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
index 14e2b35fd63..82e837b30f6 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
@@ -37,6 +37,12 @@ public static string AggregateOperationNotSupported(object? aggregateOperator, o
public static string ApplyNotSupported
=> GetString("ApplyNotSupported");
+ ///
+ /// Translating this operation requires the 'DEFAULT', which is not supported on SQLite.
+ ///
+ public static string DefaultNotSupported
+ => GetString("DefaultNotSupported");
+
///
/// '{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}', but are configured with different SRIDs.
///
@@ -74,7 +80,7 @@ public static string SequencesNotSupported
=> GetString("SequencesNotSupported");
///
- /// SQLite does not support stored procedures. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string StoredProceduresNotSupported(object? entityType)
=> string.Format(
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
index 89d12f3924a..f18928093f5 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
@@ -123,6 +123,9 @@
Translating this query requires the SQL APPLY operation, which is not supported on SQLite.
+
+ Translating this operation requires the 'DEFAULT', which is not supported on SQLite.
+
'{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}', but are configured with different SRIDs.
@@ -201,4 +204,4 @@
SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
-
+
\ No newline at end of file
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
index 665b25159d5..4977e4875f7 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
@@ -211,6 +212,24 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
return visitedExpression;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
+ {
+ // EF.Default
+ if (methodCallExpression.Method.IsEFDefaultMethod())
+ {
+ AddTranslationErrorDetails(SqliteStrings.DefaultNotSupported);
+ return QueryCompilationContext.NotTranslatedExpression;
+ }
+
+ return base.VisitMethodCall(methodCallExpression);
+ }
+
private static Type? GetProviderType(SqlExpression? expression)
=> expression == null
? null
diff --git a/src/EFCore/EF.cs b/src/EFCore/EF.cs
index 57b650f9251..6129242cd82 100644
--- a/src/EFCore/EF.cs
+++ b/src/EFCore/EF.cs
@@ -17,6 +17,9 @@ public static partial class EF
internal static readonly MethodInfo PropertyMethod
= typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Property))!;
+ internal static readonly MethodInfo DefaultMethod
+ = typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Default))!;
+
///
/// This flag is set to when code is being run from a design-time tool, such
/// as "dotnet ef" or one of the Package Manager Console PowerShell commands "Add-Migration", "Update-Database", etc.
@@ -55,6 +58,20 @@ public static TProperty Property(
[NotParameterized] string propertyName)
=> throw new InvalidOperationException(CoreStrings.PropertyMethodInvoked);
+ ///
+ /// Used set a property to its default value within or
+ /// .
+ ///
+ ///
+ /// Depending on how the property is configured, this may be , or another value defined via
+ /// or similar.
+ ///
+ /// The type of the property being set.
+ /// The default value of the property.
+ public static T Default()
+ // TODO: Update exception message
+ => throw new InvalidOperationException(CoreStrings.DefaultMethodInvoked);
+
///
/// Provides CLR methods that get translated to database functions when used in LINQ to Entities queries.
/// Calling these methods in other contexts (e.g. LINQ to Objects) will throw a .
diff --git a/src/EFCore/Infrastructure/MethodInfoExtensions.cs b/src/EFCore/Infrastructure/MethodInfoExtensions.cs
index b19bd5da776..644342577a9 100644
--- a/src/EFCore/Infrastructure/MethodInfoExtensions.cs
+++ b/src/EFCore/Infrastructure/MethodInfoExtensions.cs
@@ -25,11 +25,24 @@ public static class MethodInfoExtensions
///
/// The method.
/// if the method is ; otherwise.
- public static bool IsEFPropertyMethod(this MethodInfo? methodInfo)
- => Equals(methodInfo, EF.PropertyMethod)
- // fallback to string comparison because MethodInfo.Equals is not
- // always true in .NET Native even if methods are the same
- || methodInfo?.IsGenericMethod == true
- && methodInfo.Name == nameof(EF.Property)
- && methodInfo.DeclaringType?.FullName == EFTypeName;
+ public static bool IsEFPropertyMethod(this MethodInfo methodInfo)
+ => methodInfo.IsGenericMethod
+ && (Equals(methodInfo.GetGenericMethodDefinition(), EF.PropertyMethod)
+ // fallback to string comparison because MethodInfo.Equals is not
+ // always true in .NET Native even if methods are the same
+ || (methodInfo.Name == nameof(EF.Property)
+ && methodInfo.DeclaringType?.FullName == EFTypeName));
+
+ ///
+ /// Returns if the given method is .
+ ///
+ /// The method.
+ /// if the method is ; otherwise.
+ public static bool IsEFDefaultMethod(this MethodInfo methodInfo)
+ => methodInfo.IsGenericMethod
+ && (Equals(methodInfo.GetGenericMethodDefinition(), EF.DefaultMethod)
+ // fallback to string comparison because MethodInfo.Equals is not
+ // always true in .NET Native even if methods are the same
+ || (methodInfo.Name == nameof(EF.DefaultMethod)
+ && methodInfo.DeclaringType?.FullName == EFTypeName));
}
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 27f767ea761..de9d3009197 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -598,6 +598,12 @@ public static string DebugViewQueryStringError(object? message)
GetString("DebugViewQueryStringError", nameof(message)),
message);
+ ///
+ /// The EF.Default<T> property may only be used within Entity Framework ExecuteUpdate method.
+ ///
+ public static string DefaultMethodInvoked
+ => GetString("DefaultMethodInvoked");
+
///
/// The [DeleteBehavior] attribute may only be specified on navigation properties, and is not supported not on properties making up the foreign key.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 0ed95bdb7cf..32a8f8bbfab 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -336,6 +336,9 @@
Error creating query string: {message}.
+
+ The EF.Default<T> property may only be used within Entity Framework ExecuteUpdate method.
+
The [DeleteBehavior] attribute may only be specified on navigation properties, and is not supported not on properties making up the foreign key.
diff --git a/src/EFCore/Query/EvaluatableExpressionFilter.cs b/src/EFCore/Query/EvaluatableExpressionFilter.cs
index d3a2d0d9bf8..1dbb572dfc3 100644
--- a/src/EFCore/Query/EvaluatableExpressionFilter.cs
+++ b/src/EFCore/Query/EvaluatableExpressionFilter.cs
@@ -81,7 +81,8 @@ public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
|| Equals(method, RandomNextNoArgs)
|| Equals(method, RandomNextOneArg)
|| Equals(method, RandomNextTwoArgs)
- || method.DeclaringType == typeof(DbFunctionsExtensions))
+ || method.DeclaringType == typeof(DbFunctionsExtensions)
+ || method.IsEFDefaultMethod())
{
return false;
}
diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
index 121f3574f1b..4b90c04448f 100644
--- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
@@ -153,10 +153,7 @@ when QueryableMethods.IsAverageWithSelector(method):
var source2 = Visit(methodCallExpression.Arguments[1]);
if (source2 is ShapedQueryExpression innerShapedQueryExpression)
{
- return CheckTranslated(
- TranslateConcat(
- shapedQueryExpression,
- innerShapedQueryExpression));
+ return CheckTranslated(TranslateConcat(shapedQueryExpression, innerShapedQueryExpression));
}
break;
@@ -207,10 +204,7 @@ when QueryableMethods.IsAverageWithSelector(method):
var source2 = Visit(methodCallExpression.Arguments[1]);
if (source2 is ShapedQueryExpression innerShapedQueryExpression)
{
- return CheckTranslated(
- TranslateExcept(
- shapedQueryExpression,
- innerShapedQueryExpression));
+ return CheckTranslated(TranslateExcept(shapedQueryExpression, innerShapedQueryExpression));
}
break;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
index 5e85a43743c..7011a2f92d2 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs
@@ -21,7 +21,7 @@ public virtual Task Delete_where_hierarchy(bool async)
ss => ss.Set().Where(e => e.Name == "Great spotted kiwi"),
rowsAffectedCount: 1);
- [ConditionalTheory(Skip = "Issue#28524")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_where_hierarchy_subquery(bool async)
=> AssertDelete(
@@ -53,6 +53,35 @@ public virtual Task Delete_where_using_hierarchy_derived(bool async)
ss => ss.Set().Where(e => e.Animals.OfType().Where(a => a.CountryId > 0).Count() > 0),
rowsAffectedCount: 1);
+ [ConditionalTheory(Skip = "Issue#28525")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set()
+ .GroupBy(e => e.CountryId)
+ .Where(g => g.Count() < 3)
+ .Select(g => g.First()),
+ rowsAffectedCount: 1);
+
+ [ConditionalTheory(Skip = "Issue#26753")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First_2(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => e == ss.Set().GroupBy(e => e.CountryId)
+ .Where(g => g.Count() < 3).Select(g => g.First()).FirstOrDefault()),
+ rowsAffectedCount: 1);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => ss.Set().GroupBy(e => e.CountryId)
+ .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)
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
index 13a41526378..40227581efa 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs
@@ -7,6 +7,9 @@ public abstract class InheritanceBulkUpdatesFixtureBase : InheritanceQueryFixtur
{
protected override string StoreName => "InheritanceBulkUpdatesTest";
+ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => base.AddOptions(builder).ConfigureWarnings(w => w.Log(CoreEventId.FirstWithoutOrderByAndFilterWarning));
+
public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
index 4b491e425d7..7b90eb47c40 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs
@@ -21,7 +21,7 @@ public virtual Task Delete_where_hierarchy(bool async)
ss => ss.Set().Where(e => e.Name == "Great spotted kiwi"),
rowsAffectedCount: 1);
- [ConditionalTheory(Skip = "Issue#28524")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_where_hierarchy_subquery(bool async)
=> AssertDelete(
@@ -53,6 +53,35 @@ public virtual Task Delete_where_using_hierarchy_derived(bool async)
ss => ss.Set().Where(e => e.Animals.OfType().Where(a => a.CountryId > 0).Count() > 0),
rowsAffectedCount: 1);
+ [ConditionalTheory(Skip = "Issue#28525")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set()
+ .GroupBy(e => e.CountryId)
+ .Where( g=> g.Count() < 3)
+ .Select(g => g.First()),
+ rowsAffectedCount: 2);
+
+ [ConditionalTheory(Skip = "Issue#26753")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First_2(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => e == ss.Set().GroupBy(e => e.CountryId)
+ .Where(g => g.Count() < 3).Select(g => g.First()).FirstOrDefault()),
+ rowsAffectedCount: 2);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => ss.Set().GroupBy(e => e.CountryId)
+ .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)
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
index dc8d5cca556..38aced574f3 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
@@ -14,6 +14,14 @@ protected NorthwindBulkUpdatesTestBase(TFixture fixture)
ClearLog();
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_Where_TagWith(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set().Where(e => e.OrderID < 10300).TagWith("MyDelete"),
+ rowsAffectedCount: 140);
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_Where(bool async)
@@ -109,7 +117,7 @@ public virtual Task Delete_Where_predicate_with_GroupBy_aggregate(bool async)
.Select(g => g.First()).First().OrderID),
rowsAffectedCount: 284);
- [ConditionalTheory(Skip = "Issue#28524")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool async)
=> AssertDelete(
@@ -119,7 +127,7 @@ public virtual Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool async)
.GroupBy(o => o.CustomerID)
.Where(g => g.Count() > 9)
.Select(g => g.First()).Contains(e.Order)),
- rowsAffectedCount: 40);
+ rowsAffectedCount: 109);
[ConditionalTheory(Skip = "Issue#28525")]
[MemberData(nameof(IsAsyncData))]
@@ -132,6 +140,15 @@ public virtual Task Delete_GroupBy_Where_Select(bool async)
.Select(g => g.First()),
rowsAffectedCount: 284);
+ [ConditionalTheory(Skip = "Issue#26753")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Delete_GroupBy_Where_Select_2(bool async)
+ => AssertDelete(
+ async,
+ ss => ss.Set()
+ .Where(od => od == ss.Set().GroupBy(od => od.OrderID).Where(g => g.Count() > 5).Select(g => g.First()).FirstOrDefault()),
+ rowsAffectedCount: 284);
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async)
@@ -341,6 +358,17 @@ from o in ss.Set().Where(o => o.OrderID < od.OrderID).OrderBy(e => e.Orde
select od,
rowsAffectedCount: 74);
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_Where_set_constant_TagWith(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")).TagWith("MyUpdate"),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 8,
+ (b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Where_set_constant(bool async)
@@ -352,6 +380,17 @@ public virtual Task Update_Where_set_constant(bool async)
rowsAffectedCount: 8,
(b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_Where_set_default(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => EF.Default()),
+ rowsAffectedCount: 8,
+ (b, a) => Assert.All(a, c => Assert.Null(c.ContactName)));
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Update_Where_parameter_set_constant(bool async)
@@ -516,7 +555,7 @@ public virtual Task Update_Where_GroupBy_First_set_constant_2(bool async)
rowsAffectedCount: 1,
(b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
- [ConditionalTheory(Skip = "Issue#28524")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Where_GroupBy_First_set_constant_3(bool async)
=> AssertUpdate(
@@ -526,7 +565,7 @@ public virtual Task Update_Where_GroupBy_First_set_constant_3(bool async)
.GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.First().Customer).Contains(c)),
e => e,
s => s.SetProperty(c => c.ContactName, c => "Updated"),
- rowsAffectedCount: 1,
+ rowsAffectedCount: 24,
(b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
[ConditionalTheory]
@@ -869,7 +908,7 @@ public virtual Task Update_Where_SelectMany_subquery_set_null(bool async)
rowsAffectedCount: 35,
(b, a) => Assert.All(a, c => Assert.Null(c.OrderDate)));
- [ConditionalTheory(Skip = "Issue#28752")]
+ [ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_Where_Join_set_property_from_joined_single_result_table(bool async)
=> AssertUpdate(
@@ -877,9 +916,19 @@ public virtual Task Update_Where_Join_set_property_from_joined_single_result_tab
ss => from c in ss.Set().Where(c => c.CustomerID.StartsWith("F"))
select new { c, LastOrder = c.Orders.OrderByDescending(o => o.OrderDate).FirstOrDefault() },
e => e.c,
- s => s.SetProperty(c => c.c.City, c => c.LastOrder.OrderDate.ToString()),
- rowsAffectedCount: 35,
- (b, a) => Assert.All(a, c => Assert.NotNull(c.City)));
+ s => s.SetProperty(c => c.c.City, c => c.LastOrder.OrderDate.Value.Year.ToString()),
+ rowsAffectedCount: 8,
+ (b, a) => Assert.All(a, c =>
+ {
+ if (c.CustomerID == "FISSA")
+ {
+ Assert.Null(c.City);
+ }
+ else
+ {
+ Assert.NotNull(c.City);
+ }
+ }));
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
index cbab0c39ede..bf4c657b7ec 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs
@@ -19,6 +19,16 @@ public override Task Delete_where_hierarchy(bool async)
RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
() => base.Delete_where_hierarchy(async));
+ public override Task Delete_where_hierarchy_subquery(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
+ () => base.Delete_where_hierarchy_subquery(async));
+
+ public override Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
+ () => base.Delete_GroupBy_Where_Select_First_3(async));
+
// Keyless entities are mapped as TPH only
public override Task Update_where_keyless_entity_mapped_to_sql_query(bool async) => Task.CompletedTask;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
index 1091866fa79..f1d2a57c877 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs
@@ -19,6 +19,16 @@ public override Task Delete_where_hierarchy(bool async)
RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
() => base.Delete_where_hierarchy(async));
+ public override Task Delete_where_hierarchy_subquery(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
+ () => base.Delete_where_hierarchy_subquery(async));
+
+ public override Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"),
+ () => base.Delete_GroupBy_Where_Select_First_3(async));
+
// Keyless entities are mapped as TPH only
public override Task Update_where_keyless_entity_mapped_to_sql_query(bool async) => Task.CompletedTask;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
index 9e144a3b6a1..8eb6cc01c08 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs
@@ -19,11 +19,21 @@ public override Task Delete_where_hierarchy(bool async)
RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
() => base.Delete_where_hierarchy(async));
+ public override Task Delete_where_hierarchy_subquery(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
+ () => base.Delete_where_hierarchy_subquery(async));
+
public override Task Delete_where_hierarchy_derived(bool async)
=> AssertTranslationFailed(
RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Kiwi"),
() => base.Delete_where_hierarchy_derived(async));
+ public override Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
+ () => base.Delete_GroupBy_Where_Select_First_3(async));
+
[ConditionalTheory(Skip = "Issue#28532")]
public override Task Delete_where_using_hierarchy(bool async)
{
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
index 3d97ec4d2e4..7c6b53d8ca1 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs
@@ -19,11 +19,21 @@ public override Task Delete_where_hierarchy(bool async)
RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
() => base.Delete_where_hierarchy(async));
+ public override Task Delete_where_hierarchy_subquery(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
+ () => base.Delete_where_hierarchy_subquery(async));
+
public override Task Delete_where_hierarchy_derived(bool async)
=> AssertTranslationFailed(
RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Kiwi"),
() => base.Delete_where_hierarchy_derived(async));
+ public override Task Delete_GroupBy_Where_Select_First_3(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"),
+ () => base.Delete_GroupBy_Where_Select_First_3(async));
+
[ConditionalTheory(Skip = "FK constraint issue")]
public override Task Delete_where_using_hierarchy(bool async)
{
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs
index b5dc9ff2ce6..3f03db1c022 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs
@@ -61,6 +61,38 @@ FROM [Animals] AS [a]
WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0");
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql(
+ @"DELETE FROM [a]
+FROM [Animals] AS [a]
+WHERE [a].[CountryId] = 1 AND EXISTS (
+ SELECT 1
+ FROM [Animals] AS [a0]
+ WHERE [a0].[CountryId] = 1
+ GROUP BY [a0].[CountryId]
+ HAVING COUNT(*) < 3 AND (
+ SELECT TOP(1) [a1].[Id]
+ FROM [Animals] AS [a1]
+ WHERE [a1].[CountryId] = 1 AND [a0].[CountryId] = [a1].[CountryId]) = [a].[Id])");
+ }
+
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
@@ -72,7 +104,22 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
- AssertSql();
+ AssertSql(
+ @"@__p_0='0'
+@__p_1='3'
+
+DELETE FROM [a]
+FROM [Animals] AS [a]
+WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT [a0].[Id], [a0].[CountryId], [a0].[Discriminator], [a0].[Name], [a0].[Species], [a0].[EagleId], [a0].[IsFlightless], [a0].[Group], [a0].[FoundOn]
+ FROM [Animals] AS [a0]
+ WHERE [a0].[CountryId] = 1 AND [a0].[Name] = N'Great spotted kiwi'
+ ORDER BY [a0].[Name]
+ OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
+ ) AS [t]
+ WHERE [t].[Id] = [a].[Id])");
}
public override async Task Update_where_hierarchy(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs
index 19086fa182a..b46afb9201d 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs
@@ -61,6 +61,37 @@ FROM [Animals] AS [a]
WHERE [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0");
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql(
+ @"DELETE FROM [a]
+FROM [Animals] AS [a]
+WHERE EXISTS (
+ SELECT 1
+ FROM [Animals] AS [a0]
+ GROUP BY [a0].[CountryId]
+ HAVING COUNT(*) < 3 AND (
+ SELECT TOP(1) [a1].[Id]
+ FROM [Animals] AS [a1]
+ WHERE [a0].[CountryId] = [a1].[CountryId]) = [a].[Id])");
+ }
+
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
@@ -72,7 +103,22 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
- AssertSql();
+ AssertSql(
+ @"@__p_0='0'
+@__p_1='3'
+
+DELETE FROM [a]
+FROM [Animals] AS [a]
+WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT [a0].[Id], [a0].[CountryId], [a0].[Discriminator], [a0].[Name], [a0].[Species], [a0].[EagleId], [a0].[IsFlightless], [a0].[Group], [a0].[FoundOn]
+ FROM [Animals] AS [a0]
+ WHERE [a0].[Name] = N'Great spotted kiwi'
+ ORDER BY [a0].[Name]
+ OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
+ ) AS [t]
+ WHERE [t].[Id] = [a].[Id])");
}
public override async Task Update_where_hierarchy(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
index 3a95f571010..42ae2dac218 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
@@ -14,6 +14,18 @@ public NorthwindBulkUpdatesSqlServerTest(NorthwindBulkUpdatesSqlServerFixture TestHelpers.AssertAllMethodsOverridden(GetType());
+ public override async Task Delete_Where_TagWith(bool async)
+ {
+ await base.Delete_Where_TagWith(async);
+
+ AssertSql(
+ @"-- MyDelete
+
+DELETE FROM [o]
+FROM [Order Details] AS [o]
+WHERE [o].[OrderID] < 10300");
+ }
+
public override async Task Delete_Where(bool async)
{
await base.Delete_Where(async);
@@ -190,7 +202,18 @@ public override async Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool
{
await base.Delete_Where_predicate_with_GroupBy_aggregate_2(async);
- AssertSql();
+ AssertSql(
+ @"DELETE FROM [o]
+FROM [Order Details] AS [o]
+INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID]
+WHERE EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o1]
+ GROUP BY [o1].[CustomerID]
+ HAVING COUNT(*) > 9 AND (
+ SELECT TOP(1) [o2].[OrderID]
+ FROM [Orders] AS [o2]
+ WHERE [o1].[CustomerID] = [o2].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o2].[CustomerID] IS NULL)) = [o0].[OrderID])");
}
public override async Task Delete_GroupBy_Where_Select(bool async)
@@ -200,6 +223,13 @@ public override async Task Delete_GroupBy_Where_Select(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_2(async);
+
+ AssertSql();
+ }
+
public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async)
{
await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async);
@@ -514,6 +544,19 @@ OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
WHERE [o].[OrderID] < 10276");
}
+ public override async Task Update_Where_set_constant_TagWith(bool async)
+ {
+ await base.Update_Where_set_constant_TagWith(async);
+
+ AssertExecuteUpdateSql(
+ @"-- MyUpdate
+
+UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
public override async Task Update_Where_set_constant(bool async)
{
await base.Update_Where_set_constant(async);
@@ -525,6 +568,17 @@ FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'F%'");
}
+ public override async Task Update_Where_set_default(bool async)
+ {
+ await base.Update_Where_set_default(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = DEFAULT
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
public override async Task Update_Where_parameter_set_constant(bool async)
{
await base.Update_Where_parameter_set_constant(async);
@@ -759,7 +813,19 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async)
{
await base.Update_Where_GroupBy_First_set_constant_3(async);
- AssertExecuteUpdateSql();
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE EXISTS (
+ SELECT 1
+ FROM [Orders] AS [o]
+ GROUP BY [o].[CustomerID]
+ HAVING COUNT(*) > 11 AND (
+ SELECT TOP(1) [c0].[CustomerID]
+ FROM [Orders] AS [o0]
+ LEFT JOIN [Customers] AS [c0] ON [o0].[CustomerID] = [c0].[CustomerID]
+ WHERE [o].[CustomerID] = [o0].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL)) = [c].[CustomerID])");
}
public override async Task Update_Where_Distinct_set_constant(bool async)
@@ -1103,7 +1169,15 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res
{
await base.Update_Where_Join_set_property_from_joined_single_result_table(async);
- AssertExecuteUpdateSql();
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[City] = CONVERT(varchar(11), DATEPART(year, (
+ SELECT TOP(1) [o].[OrderDate]
+ FROM [Orders] AS [o]
+ WHERE [c].[CustomerID] = [o].[CustomerID]
+ ORDER BY [o].[OrderDate] DESC)))
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
}
public override async Task Update_Where_Join_set_property_from_joined_table(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs
index 1aff954c2aa..2599e995da5 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs
@@ -81,6 +81,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs
index 75e7110f4c4..071d1b78c3e 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs
@@ -81,6 +81,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs
index b07f2686746..d11df1e626e 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs
@@ -69,6 +69,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs
index 91ce894d821..b9b523680fd 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs
@@ -57,6 +57,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs
index 362f5e7b3e1..dcdddef09ce 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs
@@ -28,7 +28,21 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
- AssertSql();
+ AssertSql(
+ @"@__p_1='3'
+@__p_0='0'
+
+DELETE FROM ""Animals"" AS ""a""
+WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT ""a0"".""Id"", ""a0"".""CountryId"", ""a0"".""Discriminator"", ""a0"".""Name"", ""a0"".""Species"", ""a0"".""EagleId"", ""a0"".""IsFlightless"", ""a0"".""Group"", ""a0"".""FoundOn""
+ FROM ""Animals"" AS ""a0""
+ WHERE ""a0"".""CountryId"" = 1 AND ""a0"".""Name"" = 'Great spotted kiwi'
+ ORDER BY ""a0"".""Name""
+ LIMIT @__p_1 OFFSET @__p_0
+ ) AS ""t""
+ WHERE ""t"".""Id"" = ""a"".""Id"")");
}
public override async Task Delete_where_hierarchy_derived(bool async)
@@ -71,6 +85,38 @@ public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql(
+ @"DELETE FROM ""Animals"" AS ""a""
+WHERE ""a"".""CountryId"" = 1 AND EXISTS (
+ SELECT 1
+ FROM ""Animals"" AS ""a0""
+ WHERE ""a0"".""CountryId"" = 1
+ GROUP BY ""a0"".""CountryId""
+ HAVING COUNT(*) < 3 AND (
+ SELECT ""a1"".""Id""
+ FROM ""Animals"" AS ""a1""
+ WHERE ""a1"".""CountryId"" = 1 AND ""a0"".""CountryId"" = ""a1"".""CountryId""
+ LIMIT 1) = ""a"".""Id"")");
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs
index 15da6f75101..b8c70262b20 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs
@@ -28,7 +28,21 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
- AssertSql();
+ AssertSql(
+ @"@__p_1='3'
+@__p_0='0'
+
+DELETE FROM ""Animals"" AS ""a""
+WHERE EXISTS (
+ SELECT 1
+ FROM (
+ SELECT ""a0"".""Id"", ""a0"".""CountryId"", ""a0"".""Discriminator"", ""a0"".""Name"", ""a0"".""Species"", ""a0"".""EagleId"", ""a0"".""IsFlightless"", ""a0"".""Group"", ""a0"".""FoundOn""
+ FROM ""Animals"" AS ""a0""
+ WHERE ""a0"".""Name"" = 'Great spotted kiwi'
+ ORDER BY ""a0"".""Name""
+ LIMIT @__p_1 OFFSET @__p_0
+ ) AS ""t""
+ WHERE ""t"".""Id"" = ""a"".""Id"")");
}
public override async Task Delete_where_hierarchy_derived(bool async)
@@ -71,6 +85,37 @@ public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql(
+ @"DELETE FROM ""Animals"" AS ""a""
+WHERE EXISTS (
+ SELECT 1
+ FROM ""Animals"" AS ""a0""
+ GROUP BY ""a0"".""CountryId""
+ HAVING COUNT(*) < 3 AND (
+ SELECT ""a1"".""Id""
+ FROM ""Animals"" AS ""a1""
+ WHERE ""a0"".""CountryId"" = ""a1"".""CountryId""
+ LIMIT 1) = ""a"".""Id"")");
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
index dd85bb0a8b2..785289a6102 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.BulkUpdates;
@@ -16,6 +17,17 @@ public NorthwindBulkUpdatesSqliteTest(NorthwindBulkUpdatesSqliteFixture TestHelpers.AssertAllMethodsOverridden(GetType());
+ public override async Task Delete_Where_TagWith(bool async)
+ {
+ await base.Delete_Where_TagWith(async);
+
+ AssertSql(
+ @"-- MyDelete
+
+DELETE FROM ""Order Details"" AS ""o""
+WHERE ""o"".""OrderID"" < 10300");
+ }
+
public override async Task Delete_Where(bool async)
{
await base.Delete_Where(async);
@@ -190,7 +202,21 @@ public override async Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool
{
await base.Delete_Where_predicate_with_GroupBy_aggregate_2(async);
- AssertSql();
+ AssertSql(
+ @"DELETE FROM ""Order Details"" AS ""o""
+WHERE EXISTS (
+ SELECT 1
+ FROM ""Order Details"" AS ""o0""
+ INNER JOIN ""Orders"" AS ""o1"" ON ""o0"".""OrderID"" = ""o1"".""OrderID""
+ WHERE EXISTS (
+ SELECT 1
+ FROM ""Orders"" AS ""o2""
+ GROUP BY ""o2"".""CustomerID""
+ HAVING COUNT(*) > 9 AND (
+ SELECT ""o3"".""OrderID""
+ FROM ""Orders"" AS ""o3""
+ WHERE ""o2"".""CustomerID"" = ""o3"".""CustomerID"" OR (""o2"".""CustomerID"" IS NULL AND ""o3"".""CustomerID"" IS NULL)
+ LIMIT 1) = ""o1"".""OrderID"") AND ""o0"".""OrderID"" = ""o"".""OrderID"" AND ""o0"".""ProductID"" = ""o"".""ProductID"")");
}
public override async Task Delete_GroupBy_Where_Select(bool async)
@@ -200,6 +226,13 @@ public override async Task Delete_GroupBy_Where_Select(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_2(async);
+
+ AssertSql();
+ }
+
public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async)
{
await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async);
@@ -495,6 +528,18 @@ public override async Task Delete_with_outer_apply(bool async)
SqliteStrings.ApplyNotSupported,
(await Assert.ThrowsAsync(() => base.Delete_with_outer_apply(async))).Message);
+ public override async Task Update_Where_set_constant_TagWith(bool async)
+ {
+ await base.Update_Where_set_constant_TagWith(async);
+
+ AssertExecuteUpdateSql(
+ @"-- MyUpdate
+
+UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE ""c"".""CustomerID"" LIKE 'F%'");
+ }
+
public override async Task Update_Where_set_constant(bool async)
{
await base.Update_Where_set_constant(async);
@@ -505,6 +550,12 @@ public override async Task Update_Where_set_constant(bool async)
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
+ public override Task Update_Where_set_default(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.UnableToTranslateSetProperty(
+ "c => c.ContactName", "c => EF.Default()", SqliteStrings.DefaultNotSupported),
+ () => base.Update_Where_set_default(async));
+
public override async Task Update_Where_parameter_set_constant(bool async)
{
await base.Update_Where_parameter_set_constant(async);
@@ -741,7 +792,19 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async)
{
await base.Update_Where_GroupBy_First_set_constant_3(async);
- AssertExecuteUpdateSql();
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE EXISTS (
+ SELECT 1
+ FROM ""Orders"" AS ""o""
+ GROUP BY ""o"".""CustomerID""
+ HAVING COUNT(*) > 11 AND (
+ SELECT ""c0"".""CustomerID""
+ FROM ""Orders"" AS ""o0""
+ LEFT JOIN ""Customers"" AS ""c0"" ON ""o0"".""CustomerID"" = ""c0"".""CustomerID""
+ WHERE ""o"".""CustomerID"" = ""o0"".""CustomerID"" OR (""o"".""CustomerID"" IS NULL AND ""o0"".""CustomerID"" IS NULL)
+ LIMIT 1) = ""c"".""CustomerID"")");
}
public override async Task Update_Where_Distinct_set_constant(bool async)
@@ -1065,7 +1128,15 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res
{
await base.Update_Where_Join_set_property_from_joined_single_result_table(async);
- AssertExecuteUpdateSql();
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""City"" = CAST(CAST(strftime('%Y', (
+ SELECT ""o"".""OrderDate""
+ FROM ""Orders"" AS ""o""
+ WHERE ""c"".""CustomerID"" = ""o"".""CustomerID""
+ ORDER BY ""o"".""OrderDate"" DESC
+ LIMIT 1)) AS INTEGER) AS TEXT)
+WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
public override async Task Update_Where_Join_set_property_from_joined_table(bool async)
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs
index 5ff49896ae8..8124cee18f7 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs
@@ -78,6 +78,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs
index 701c9f8435c..f723adff3d3 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs
@@ -78,6 +78,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs
index 162ac651920..c19e0492324 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs
@@ -69,6 +69,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Update_where_hierarchy(bool async)
{
await base.Update_where_hierarchy(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs
index 6c276c2076f..7182b55da68 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs
@@ -43,6 +43,27 @@ public override async Task Delete_where_using_hierarchy_derived(bool async)
AssertSql();
}
+ public override async Task Delete_GroupBy_Where_Select_First(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_2(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_2(async);
+
+ AssertSql();
+ }
+
+ public override async Task Delete_GroupBy_Where_Select_First_3(bool async)
+ {
+ await base.Delete_GroupBy_Where_Select_First_3(async);
+
+ AssertSql();
+ }
+
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);