diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
new file mode 100644
index 00000000000..bae6307727a
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs
@@ -0,0 +1,163 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+
+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)
+ {
+ if (source.ShaperExpression is IncludeExpression includeExpression)
+ {
+ source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
+ }
+
+ if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)
+ {
+ AddTranslationErrorDetails(RelationalStrings.ExecuteDeleteOnNonEntityType);
+ return null;
+ }
+
+ var mappingStrategy = entityType.GetMappingStrategy();
+ if (mappingStrategy == RelationalAnnotationNames.TptMappingStrategy)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnTPT(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ return null;
+ }
+
+ if (mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy
+ && entityType.GetDirectlyDerivedTypes().Any())
+ {
+ // We allow TPC is it is leaf type
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnTPC(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ return null;
+ }
+
+ if (entityType.GetViewOrTableMappings().Count() != 1)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnEntitySplitting(
+ nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
+ return null;
+ }
+
+ // First, check if the provider has a native translation for the delete represented by the select expression.
+ // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).
+ // Providers may override IsValidSelectExpressionForExecuteDelete to add support for more cases via provider-specific DELETE syntax.
+ var selectExpression = (SelectExpression)source.QueryExpression;
+ if (IsValidSelectExpressionForExecuteDelete(selectExpression, shaper, out var tableExpression))
+ {
+ if (AreOtherNonOwnedEntityTypesInTheTable(entityType.GetRootType(), tableExpression.Table))
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteDeleteOnTableSplitting(tableExpression.Table.SchemaQualifiedName));
+
+ return null;
+ }
+
+ selectExpression.ReplaceProjection(new List());
+ selectExpression.ApplyProjection();
+
+ return new NonQueryExpression(new DeleteExpression(tableExpression, selectExpression));
+
+ static bool AreOtherNonOwnedEntityTypesInTheTable(IEntityType rootType, ITableBase table)
+ {
+ foreach (var entityTypeMapping in table.EntityTypeMappings)
+ {
+ var typeBase = entityTypeMapping.TypeBase;
+ if ((entityTypeMapping.IsSharedTablePrincipal == true
+ && typeBase != rootType)
+ || (entityTypeMapping.IsSharedTablePrincipal == false
+ && typeBase is IEntityType entityType
+ && entityType.GetRootType() != rootType
+ && !entityType.IsOwned()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ // The provider doesn't natively support the delete.
+ // As a fallback, we place the original query in a Contains subquery, which will get translated via the regular entity equality/
+ // containment mechanism (InExpression for non-composite keys, Any for composite keys)
+ var pk = entityType.FindPrimaryKey();
+ if (pk == null)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
+ nameof(RelationalQueryableExtensions.ExecuteDelete),
+ entityType.DisplayName()));
+ return null;
+ }
+
+ var clrType = entityType.ClrType;
+ var entityParameter = Expression.Parameter(clrType);
+ var predicateBody = Expression.Call(QueryableMethods.Contains.MakeGenericMethod(clrType), source, entityParameter);
+
+ var newSource = Expression.Call(
+ QueryableMethods.Where.MakeGenericMethod(clrType),
+ new EntityQueryRootExpression(entityType),
+ Expression.Quote(Expression.Lambda(predicateBody, entityParameter)));
+
+ return TranslateExecuteDelete((ShapedQueryExpression)Visit(newSource));
+ }
+
+ ///
+ /// Checks weather the current select expression can be used as-is for executing a delete operation, or whether it must be pushed
+ /// down into a subquery.
+ ///
+ ///
+ ///
+ /// By default, only single-table select expressions are supported, and optionally with a predicate.
+ ///
+ ///
+ /// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
+ /// When doing this, VisitDelete must also be overridden in the provider's QuerySqlGenerator to add SQL generation support for
+ /// the feature.
+ ///
+ ///
+ /// The select expression to validate.
+ /// The structural type shaper expression on which the delete operation is being applied.
+ /// The table expression from which rows are being deleted.
+ ///
+ /// Returns if the current select expression can be used for delete as-is, otherwise.
+ ///
+ protected virtual bool IsValidSelectExpressionForExecuteDelete(
+ SelectExpression selectExpression,
+ StructuralTypeShaperExpression shaper,
+ [NotNullWhen(true)] out TableExpression? tableExpression)
+ {
+ if (selectExpression is
+ {
+ Tables: [TableExpression expression],
+ Orderings: [],
+ Offset: null,
+ Limit: null,
+ GroupBy: [],
+ Having: null
+ })
+ {
+ tableExpression = expression;
+
+ return true;
+ }
+
+ tableExpression = null;
+ return false;
+ }
+}
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
new file mode 100644
index 00000000000..9473fb1ab9a
--- /dev/null
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs
@@ -0,0 +1,617 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Query.Internal;
+using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public partial class RelationalQueryableMethodTranslatingExpressionVisitor
+{
+ private const string ExecuteUpdateRuntimeParameterPrefix = QueryCompilationContext.QueryParameterPrefix + "complex_type_";
+
+ 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)
+ {
+ // 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.
+ if (source.ShaperExpression is IncludeExpression includeExpression)
+ {
+ source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
+ }
+
+ var setters = new List<(LambdaExpression PropertySelector, Expression ValueExpression)>();
+ PopulateSetPropertyCalls(setPropertyCalls.Body, setters, setPropertyCalls.Parameters[0]);
+ if (TranslationErrorDetails != null)
+ {
+ return null;
+ }
+
+ if (setters.Count == 0)
+ {
+ AddTranslationErrorDetails(RelationalStrings.NoSetPropertyInvocation);
+ return null;
+ }
+
+ // Translate the setters: the left (property) selectors get translated to ColumnExpressions, the right (value) selectors to
+ // arbitrary SqlExpressions.
+ // Note that if the query isn't natively supported, we'll do a pushdown (see PushdownWithPkInnerJoinPredicate below); if that
+ // happens, we'll have to re-translate the setters over the new query (which includes a JOIN). However, we still translate here
+ // since we need the target table in order to perform the check below.
+ if (!TranslateSetters(source, setters, out var translatedSetters, out var targetTable))
+ {
+ return null;
+ }
+
+ // Check if the provider has a native translation for the update represented by the select expression.
+ // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).
+ // Providers may override IsValidSelectExpressionForExecuteUpdate to add support for more cases via provider-specific UPDATE syntax.
+ var selectExpression = (SelectExpression)source.QueryExpression;
+ if (IsValidSelectExpressionForExecuteUpdate(selectExpression, targetTable, out var tableExpression))
+ {
+ selectExpression.ReplaceProjection(new List());
+ selectExpression.ApplyProjection();
+
+ return new NonQueryExpression(new UpdateExpression(tableExpression, selectExpression, translatedSetters));
+ }
+
+ return PushdownWithPkInnerJoinPredicate();
+
+ void PopulateSetPropertyCalls(
+ Expression expression,
+ List<(LambdaExpression, Expression)> list,
+ ParameterExpression parameter)
+ {
+ switch (expression)
+ {
+ case ParameterExpression p
+ when parameter == p:
+ break;
+
+ case MethodCallExpression
+ {
+ Method:
+ {
+ IsGenericMethod: true,
+ Name: nameof(SetPropertyCalls.SetProperty),
+ DeclaringType.IsGenericType: true
+ }
+ } methodCallExpression
+ when methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(SetPropertyCalls<>):
+ list.Add(((LambdaExpression)methodCallExpression.Arguments[0], methodCallExpression.Arguments[1]));
+
+ PopulateSetPropertyCalls(methodCallExpression.Object!, list, parameter);
+
+ break;
+
+ default:
+ AddTranslationErrorDetails(RelationalStrings.InvalidArgumentToExecuteUpdate);
+ break;
+ }
+ }
+
+ bool TranslateSetters(
+ ShapedQueryExpression source,
+ List<(LambdaExpression PropertySelector, Expression ValueExpression)> setters,
+ [NotNullWhen(true)] out List? translatedSetters,
+ [NotNullWhen(true)] out TableExpressionBase? targetTable)
+ {
+ var selectExpression = (SelectExpression)source.QueryExpression;
+
+ targetTable = null;
+ TableExpressionBase? tempTargetTable = null;
+ var tempTranslatedSetters = new List();
+ translatedSetters = null;
+
+ LambdaExpression? propertySelector;
+ Expression? targetTablePropertySelector = null;
+
+ for (var i = 0; i < setters.Count; i++)
+ {
+ (propertySelector, var valueSelector) = setters[i];
+ var propertySelectorBody = RemapLambdaBody(source, propertySelector).UnwrapTypeConversion(out _);
+
+ switch (_sqlTranslator.TranslateProjection(propertySelectorBody))
+ {
+ case ColumnExpression column:
+ {
+ if (!IsColumnOnSameTable(column, propertySelector)
+ || TranslateSqlSetterValueSelector(source, valueSelector, column, selectExpression) is not SqlExpression
+ translatedValueSelector)
+ {
+ return false;
+ }
+
+ tempTranslatedSetters.Add(new(column, translatedValueSelector));
+ break;
+ }
+
+ // TODO: This is for column flattening; implement JSON complex type support as well.
+ case StructuralTypeShaperExpression
+ {
+ StructuralType: IComplexType,
+ ValueBufferExpression: StructuralTypeProjectionExpression
+ } shaper:
+ {
+ if (TranslateSetterValueSelector(source, valueSelector, shaper.Type) is not Expression translatedValueSelector
+ || !TryProcessComplexType(shaper, translatedValueSelector))
+ {
+ return false;
+ }
+
+ break;
+ }
+
+ default:
+ AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(propertySelector.Print()));
+ return false;
+ }
+ }
+
+ targetTable = tempTargetTable;
+ translatedSetters = tempTranslatedSetters;
+
+ Check.DebugAssert(targetTable is not null, "Target table should have a value");
+
+ if (targetTable is TpcTablesExpression tpcTablesExpression)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnTPC(
+ nameof(RelationalQueryableExtensions.ExecuteUpdate), tpcTablesExpression.EntityType.DisplayName()));
+ return false;
+ }
+
+ return true;
+
+ bool IsColumnOnSameTable(ColumnExpression column, LambdaExpression propertySelector)
+ {
+ if (tempTargetTable is null)
+ {
+ tempTargetTable = column.Table;
+ targetTablePropertySelector = propertySelector;
+ }
+ else if (!ReferenceEquals(column.Table, tempTargetTable))
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.MultipleTablesInExecuteUpdate(
+ propertySelector.Print(), targetTablePropertySelector!.Print()));
+ return false;
+ }
+
+ return true;
+ }
+
+ bool TryProcessComplexType(StructuralTypeShaperExpression shaperExpression, Expression valueExpression)
+ {
+ if (shaperExpression.StructuralType is not IComplexType complexType
+ || shaperExpression.ValueBufferExpression is not StructuralTypeProjectionExpression projection)
+ {
+ return false;
+ }
+
+ foreach (var property in complexType.GetProperties())
+ {
+ var column = projection.BindProperty(property);
+ if (!IsColumnOnSameTable(column, propertySelector))
+ {
+ return false;
+ }
+
+ var rewrittenValueSelector = CreatePropertyAccessExpression(valueExpression, property);
+ if (TranslateSqlSetterValueSelector(source, rewrittenValueSelector, column, selectExpression) is not SqlExpression
+ translatedValueSelector)
+ {
+ return false;
+ }
+
+ tempTranslatedSetters.Add(new(column, translatedValueSelector));
+ }
+
+ foreach (var complexProperty in complexType.GetComplexProperties())
+ {
+ // Note that TranslateProjection currently returns null for StructuralTypeReferenceExpression with a subquery (as
+ // opposed to a parameter); this ensures that we don't generate an efficient translation where the subquery is
+ // duplicated for every property on the complex type.
+ // TODO: Make this work by using a common table expression (CTE)
+
+ var nestedShaperExpression = projection.BindComplexProperty(complexProperty);
+ var nestedValueExpression = CreateComplexPropertyAccessExpression(valueExpression, complexProperty);
+ if (!TryProcessComplexType(nestedShaperExpression, nestedValueExpression))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ Expression CreatePropertyAccessExpression(Expression target, IProperty property)
+ {
+ return target is LambdaExpression lambda
+ ? Expression.Lambda(Core(lambda.Body, property), lambda.Parameters[0])
+ : Core(target, property);
+
+ Expression Core(Expression target, IProperty property)
+ {
+ switch (target)
+ {
+ case SqlConstantExpression constantExpression:
+ return Expression.Constant(
+ constantExpression.Value is null
+ ? null
+ : property.GetGetter().GetClrValue(constantExpression.Value),
+ property.ClrType.MakeNullable());
+
+ case SqlParameterExpression parameterExpression
+ when parameterExpression.Name.StartsWith(
+ QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal):
+ {
+ var lambda = Expression.Lambda(
+ Expression.Call(
+ ParameterValueExtractorMethod.MakeGenericMethod(property.ClrType.MakeNullable()),
+ QueryCompilationContext.QueryContextParameter,
+ Expression.Constant(parameterExpression.Name, typeof(string)),
+ Expression.Constant(null, typeof(List)),
+ Expression.Constant(property, typeof(IProperty))),
+ QueryCompilationContext.QueryContextParameter);
+
+ var newParameterName =
+ $"{ExecuteUpdateRuntimeParameterPrefix}"
+ + $"{parameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}";
+
+ return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda);
+ }
+
+ case ParameterBasedComplexPropertyChainExpression chainExpression:
+ {
+ var lambda = Expression.Lambda(
+ Expression.Call(
+ ParameterValueExtractorMethod.MakeGenericMethod(property.ClrType.MakeNullable()),
+ QueryCompilationContext.QueryContextParameter,
+ Expression.Constant(chainExpression.ParameterExpression.Name, typeof(string)),
+ Expression.Constant(chainExpression.ComplexPropertyChain, typeof(List)),
+ Expression.Constant(property, typeof(IProperty))),
+ QueryCompilationContext.QueryContextParameter);
+
+ var newParameterName =
+ $"{ExecuteUpdateRuntimeParameterPrefix}"
+ + $"{chainExpression.ParameterExpression.Name![QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}";
+
+ return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda);
+ }
+
+ case MemberInitExpression memberInitExpression
+ when memberInitExpression.Bindings.SingleOrDefault(
+ mb => mb.Member.Name == property.Name) is MemberAssignment memberAssignment:
+ return memberAssignment.Expression;
+
+ default:
+ return target.CreateEFPropertyExpression(property);
+ }
+ }
+ }
+
+ Expression CreateComplexPropertyAccessExpression(Expression target, IComplexProperty complexProperty)
+ {
+ return target is LambdaExpression lambda
+ ? Expression.Lambda(Core(lambda.Body, complexProperty), lambda.Parameters[0])
+ : Core(target, complexProperty);
+
+ Expression Core(Expression target, IComplexProperty complexProperty)
+ => target switch
+ {
+ SqlConstantExpression constant => _sqlExpressionFactory.Constant(
+ constant.Value is null ? null : complexProperty.GetGetter().GetClrValue(constant.Value),
+ complexProperty.ClrType.MakeNullable()),
+
+ SqlParameterExpression parameter
+ when parameter.Name.StartsWith(QueryCompilationContext.QueryParameterPrefix, StringComparison.Ordinal)
+ => new ParameterBasedComplexPropertyChainExpression(parameter, complexProperty),
+
+ StructuralTypeShaperExpression
+ {
+ StructuralType: IComplexType,
+ ValueBufferExpression: StructuralTypeProjectionExpression projection
+ }
+ => projection.BindComplexProperty(complexProperty),
+
+ _ => throw new UnreachableException()
+ };
+ }
+ }
+
+ SqlExpression? TranslateSqlSetterValueSelector(
+ ShapedQueryExpression source,
+ Expression valueSelector,
+ ColumnExpression column,
+ SelectExpression selectExpression)
+ {
+ if (TranslateSetterValueSelector(source, valueSelector, column.Type) is not SqlExpression translatedSelector)
+ {
+ AddTranslationErrorDetails(RelationalStrings.InvalidValueInSetProperty(valueSelector.Print()));
+ return null;
+ }
+
+ // Apply the type mapping of the column (translated from the property selector above) to the value,
+ // and apply alias uniquification to it.
+ translatedSelector = _sqlExpressionFactory.ApplyTypeMapping(translatedSelector, column.TypeMapping);
+ translatedSelector = selectExpression.AssignUniqueAliases(translatedSelector);
+ return translatedSelector;
+ }
+
+ Expression? TranslateSetterValueSelector(ShapedQueryExpression source, Expression valueSelector, Type propertyType)
+ {
+ var remappedValueSelector = valueSelector is LambdaExpression lambdaExpression
+ ? RemapLambdaBody(source, lambdaExpression)
+ : valueSelector;
+
+ if (remappedValueSelector.Type != propertyType)
+ {
+ remappedValueSelector = Expression.Convert(remappedValueSelector, propertyType);
+ }
+
+ if (_sqlTranslator.TranslateProjection(remappedValueSelector, applyDefaultTypeMapping: false) is not Expression
+ translatedValueSelector)
+ {
+ AddTranslationErrorDetails(RelationalStrings.InvalidValueInSetProperty(valueSelector.Print()));
+ return null;
+ }
+
+ return translatedValueSelector;
+ }
+
+ NonQueryExpression? PushdownWithPkInnerJoinPredicate()
+ {
+ // The provider doesn't natively support the update.
+ // As a fallback, we place the original query in a subquery and user an INNER JOIN on the primary key columns.
+
+ // Note that unlike with ExecuteDelete, we cannot use a Contains subquery (which would produce the simpler
+ // WHERE Id IN (SELECT ...) syntax), since we allow projecting out to arbitrary shapes (e.g. anonymous types) before the
+ // ExecuteUpdate.
+
+ // To rewrite the query, we need to know the primary key properties, which requires getting the entity type.
+ // Although there may be several entity types involved, we've already verified that they all map to the same table.
+ // Since we don't support table sharing of multiple entity types with different keys, simply get the entity type and key from
+ // the first property selector.
+
+ // The following mechanism for extracting the entity type from property selectors only supports simple member access,
+ // EF.Function, etc. We also unwrap casts to interface/base class (#29618). Note that owned IncludeExpressions have already
+ // been pruned from the source before remapping the lambda (#28727).
+
+ var firstPropertySelector = setters[0].PropertySelector;
+ var shaper = RemapLambdaBody(source, firstPropertySelector).UnwrapTypeConversion(out _) switch
+ {
+ MemberExpression { Expression : not null } memberExpression
+ when memberExpression.Expression.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
+ => s,
+
+ MethodCallExpression mce when mce.TryGetEFPropertyArguments(out var source, out _)
+ && source.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
+ => s,
+
+ MethodCallExpression mce when mce.TryGetIndexerArguments(RelationalDependencies.Model, out var source2, out _)
+ && source2.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
+ => s,
+
+ _ => null
+ };
+
+ if (shaper is null)
+ {
+ AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(firstPropertySelector));
+ return null;
+ }
+
+ if (shaper.StructuralType is not IEntityType entityType)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteUpdateSubqueryNotSupportedOverComplexTypes(shaper.StructuralType.DisplayName()));
+ return null;
+ }
+
+ if (entityType.FindPrimaryKey() is not IKey pk)
+ {
+ AddTranslationErrorDetails(
+ RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
+ nameof(RelationalQueryableExtensions.ExecuteUpdate),
+ entityType.DisplayName()));
+ return null;
+ }
+
+ // Generate the INNER JOIN around the original query, on the PK properties.
+ var outer = (ShapedQueryExpression)Visit(new EntityQueryRootExpression(entityType));
+ var inner = source;
+ var outerParameter = Expression.Parameter(entityType.ClrType);
+ var outerKeySelector = Expression.Lambda(outerParameter.CreateKeyValuesExpression(pk.Properties), outerParameter);
+ var firstPropertyLambdaExpression = setters[0].Item1;
+ var entitySource = GetEntitySource(RelationalDependencies.Model, firstPropertyLambdaExpression.Body);
+ var innerKeySelector = Expression.Lambda(
+ entitySource.CreateKeyValuesExpression(pk.Properties), firstPropertyLambdaExpression.Parameters);
+
+ var joinPredicate = CreateJoinPredicate(outer, outerKeySelector, inner, innerKeySelector);
+
+ Check.DebugAssert(joinPredicate != null, "Join predicate shouldn't be null");
+
+ var outerSelectExpression = (SelectExpression)outer.QueryExpression;
+ var outerShaperExpression = outerSelectExpression.AddInnerJoin(inner, joinPredicate, outer.ShaperExpression);
+ outer = outer.UpdateShaperExpression(outerShaperExpression);
+ var transparentIdentifierType = outer.ShaperExpression.Type;
+ var transparentIdentifierParameter = Expression.Parameter(transparentIdentifierType);
+
+ var propertyReplacement = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Outer");
+ var valueReplacement = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Inner");
+ for (var i = 0; i < setters.Count; i++)
+ {
+ var (propertyExpression, valueExpression) = setters[i];
+ propertyExpression = Expression.Lambda(
+ ReplacingExpressionVisitor.Replace(
+ ReplacingExpressionVisitor.Replace(
+ firstPropertyLambdaExpression.Parameters[0],
+ propertyExpression.Parameters[0],
+ entitySource),
+ propertyReplacement, propertyExpression.Body),
+ transparentIdentifierParameter);
+
+ valueExpression = valueExpression is LambdaExpression lambdaExpression
+ ? Expression.Lambda(
+ ReplacingExpressionVisitor.Replace(lambdaExpression.Parameters[0], valueReplacement, lambdaExpression.Body),
+ transparentIdentifierParameter)
+ : valueExpression;
+
+ setters[i] = (propertyExpression, valueExpression);
+ }
+
+ tableExpression = (TableExpression)outerSelectExpression.Tables[0];
+
+ // Re-translate the property selectors to get column expressions pointing to the new outer select expression (the original one
+ // has been pushed down into a subquery).
+ if (!TranslateSetters(outer, setters, out var translatedSetters, out _))
+ {
+ return null;
+ }
+
+ outerSelectExpression.ReplaceProjection(new List());
+ outerSelectExpression.ApplyProjection();
+ return new NonQueryExpression(new UpdateExpression(tableExpression, outerSelectExpression, translatedSetters));
+ }
+
+ static Expression GetEntitySource(IModel model, Expression propertyAccessExpression)
+ {
+ propertyAccessExpression = propertyAccessExpression.UnwrapTypeConversion(out _);
+ if (propertyAccessExpression is MethodCallExpression mce)
+ {
+ if (mce.TryGetEFPropertyArguments(out var source, out _))
+ {
+ return source;
+ }
+
+ if (mce.TryGetIndexerArguments(model, out var source2, out _))
+ {
+ return source2;
+ }
+ }
+
+ return ((MemberExpression)propertyAccessExpression).Expression!;
+ }
+ }
+
+ ///
+ /// Validates if the current select expression can be used for execute update operation or it requires to be joined as a subquery.
+ ///
+ ///
+ ///
+ /// By default, only multi-table select expressions are supported, and optionally with a predicate.
+ ///
+ ///
+ /// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
+ /// When doing this, VisitUpdate must also be overridden in the provider's QuerySqlGenerator to add SQL generation support for
+ /// the feature.
+ ///
+ ///
+ /// The select expression to validate.
+ /// The target table containing the rows to be updated.
+ ///
+ /// The table expression corresponding to the provided , containing the rows to be updated.
+ ///
+ ///
+ /// Returns if the current select expression can be used for update as-is, otherwise.
+ ///
+ protected virtual bool IsValidSelectExpressionForExecuteUpdate(
+ SelectExpression selectExpression,
+ TableExpressionBase targetTable,
+ [NotNullWhen(true)] out TableExpression? tableExpression)
+ {
+ tableExpression = null;
+ if (selectExpression is
+ {
+ Offset: null,
+ Limit: null,
+ IsDistinct: false,
+ GroupBy: [],
+ Having: null,
+ Orderings: [],
+ Tables.Count: > 0
+ })
+ {
+ if (selectExpression.Tables.Count > 1)
+ {
+ // If the table we are looking for is the first table, then we need to verify whether we can lift the next table in FROM
+ // clause
+ if (ReferenceEquals(selectExpression.Tables[0], targetTable)
+ && selectExpression.Tables[1] is not InnerJoinExpression and not CrossJoinExpression)
+ {
+ return false;
+ }
+
+ if (targetTable is JoinExpressionBase joinExpressionBase)
+ {
+ targetTable = joinExpressionBase.Table;
+ }
+ }
+
+ if (targetTable is TableExpression te)
+ {
+ tableExpression = te;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static T? ParameterValueExtractor(
+ QueryContext context,
+ string baseParameterName,
+ List? complexPropertyChain,
+ IProperty property)
+ {
+ var baseValue = context.ParameterValues[baseParameterName];
+
+ if (complexPropertyChain is not null)
+ {
+ foreach (var complexProperty in complexPropertyChain)
+ {
+ if (baseValue is null)
+ {
+ break;
+ }
+
+ baseValue = complexProperty.GetGetter().GetClrValue(baseValue);
+ }
+ }
+
+ return baseValue == null ? (T?)(object?)null : (T?)property.GetGetter().GetClrValue(baseValue);
+ }
+
+ private sealed class ParameterBasedComplexPropertyChainExpression : Expression
+ {
+ public ParameterBasedComplexPropertyChainExpression(
+ SqlParameterExpression parameterExpression,
+ IComplexProperty firstComplexProperty)
+ {
+ ParameterExpression = parameterExpression;
+ ComplexPropertyChain = new List { firstComplexProperty };
+ }
+
+ public SqlParameterExpression ParameterExpression { get; }
+ public List ComplexPropertyChain { get; }
+ }
+}
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index 62d9e026b0c..c13870e590e 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -9,7 +9,7 @@
namespace Microsoft.EntityFrameworkCore.Query;
///
-public class RelationalQueryableMethodTranslatingExpressionVisitor : QueryableMethodTranslatingExpressionVisitor
+public partial class RelationalQueryableMethodTranslatingExpressionVisitor : QueryableMethodTranslatingExpressionVisitor
{
private const string SqlQuerySingleColumnAlias = "Value";
private const string ValuesOrderingColumnName = "_ord", ValuesValueColumnName = "Value";
@@ -1259,532 +1259,6 @@ protected override ShapedQueryExpression TranslateUnion(ShapedQueryExpression so
return source;
}
- ///
- /// 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)
- {
- if (source.ShaperExpression is IncludeExpression includeExpression)
- {
- source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
- }
-
- if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)
- {
- AddTranslationErrorDetails(RelationalStrings.ExecuteDeleteOnNonEntityType);
- return null;
- }
-
- var mappingStrategy = entityType.GetMappingStrategy();
- if (mappingStrategy == RelationalAnnotationNames.TptMappingStrategy)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnTPT(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
- return null;
- }
-
- if (mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy
- && entityType.GetDirectlyDerivedTypes().Any())
- {
- // We allow TPC is it is leaf type
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnTPC(nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
- return null;
- }
-
- if (entityType.GetViewOrTableMappings().Count() != 1)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnEntitySplitting(
- nameof(RelationalQueryableExtensions.ExecuteDelete), entityType.DisplayName()));
- return null;
- }
-
- // First, check if the provider has a native translation for the delete represented by the select expression.
- // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).
- // Providers may override IsValidSelectExpressionForExecuteDelete to add support for more cases via provider-specific DELETE syntax.
- var selectExpression = (SelectExpression)source.QueryExpression;
- if (IsValidSelectExpressionForExecuteDelete(selectExpression, shaper, out var tableExpression))
- {
- if (AreOtherNonOwnedEntityTypesInTheTable(entityType.GetRootType(), tableExpression.Table))
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteDeleteOnTableSplitting(tableExpression.Table.SchemaQualifiedName));
-
- return null;
- }
-
- selectExpression.ReplaceProjection(new List());
- selectExpression.ApplyProjection();
-
- return new NonQueryExpression(new DeleteExpression(tableExpression, selectExpression));
-
- static bool AreOtherNonOwnedEntityTypesInTheTable(IEntityType rootType, ITableBase table)
- {
- foreach (var entityTypeMapping in table.EntityTypeMappings)
- {
- var typeBase = entityTypeMapping.TypeBase;
- if ((entityTypeMapping.IsSharedTablePrincipal == true
- && typeBase != rootType)
- || (entityTypeMapping.IsSharedTablePrincipal == false
- && typeBase is IEntityType entityType
- && entityType.GetRootType() != rootType
- && !entityType.IsOwned()))
- {
- return true;
- }
- }
-
- return false;
- }
- }
-
- // The provider doesn't natively support the delete.
- // As a fallback, we place the original query in a Contains subquery, which will get translated via the regular entity equality/
- // containment mechanism (InExpression for non-composite keys, Any for composite keys)
- var pk = entityType.FindPrimaryKey();
- if (pk == null)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
- nameof(RelationalQueryableExtensions.ExecuteDelete),
- entityType.DisplayName()));
- return null;
- }
-
- var clrType = entityType.ClrType;
- var entityParameter = Expression.Parameter(clrType);
- var predicateBody = Expression.Call(QueryableMethods.Contains.MakeGenericMethod(clrType), source, entityParameter);
-
- var newSource = Expression.Call(
- QueryableMethods.Where.MakeGenericMethod(clrType),
- new EntityQueryRootExpression(entityType),
- Expression.Quote(Expression.Lambda(predicateBody, entityParameter)));
-
- return TranslateExecuteDelete((ShapedQueryExpression)Visit(newSource));
- }
-
- ///
- /// 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)
- {
- // 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.
- if (source.ShaperExpression is IncludeExpression includeExpression)
- {
- source = source.UpdateShaperExpression(PruneIncludes(includeExpression));
- }
-
- var propertyValueLambdaExpressions = new List<(LambdaExpression PropertySelector, Expression ValueExpression)>();
- PopulateSetPropertyCalls(setPropertyCalls.Body, propertyValueLambdaExpressions, setPropertyCalls.Parameters[0]);
- if (TranslationErrorDetails != null)
- {
- return null;
- }
-
- if (propertyValueLambdaExpressions.Count == 0)
- {
- AddTranslationErrorDetails(RelationalStrings.NoSetPropertyInvocation);
- return null;
- }
-
- // Go over the SetProperty calls, and translate the property selectors (left lambda).
- // The property selectors should get translated to ColumnExpressions (otherwise they' invalid - columns are what we need to update).
- // All columns must also refer to the same table (since that's how SQL UPDATE works), extract that target table from the translated
- // columns and validate that only one table is being referenced.
- // Note that we don't translate the value expressions in this pass, since if the query is complicated, we may need to do a pushdown
- // (see PushdownWithPkInnerJoinPredicate below); so we defer translation until we have the final source/select. For the property
- // selectors we need to translate now since we need the table.
- TableExpressionBase? targetTable = null;
- Expression? targetTablePropertySelector = null;
- var columns = new ColumnExpression[propertyValueLambdaExpressions.Count];
- for (var i = 0; i < propertyValueLambdaExpressions.Count; i++)
- {
- var (propertySelector, _) = propertyValueLambdaExpressions[i];
- var propertySelectorBody = RemapLambdaBody(source, propertySelector).UnwrapTypeConversion(out _);
- if (_sqlTranslator.Translate(propertySelectorBody) is not ColumnExpression column)
- {
- AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(propertySelector.Print()));
- return null;
- }
-
- if (targetTable is null)
- {
- targetTable = column.Table;
- targetTablePropertySelector = propertySelector;
- }
- else if (!ReferenceEquals(column.Table, targetTable))
- {
- AddTranslationErrorDetails(
- RelationalStrings.MultipleTablesInExecuteUpdate(propertySelector.Print(), targetTablePropertySelector!.Print()));
- return null;
- }
-
- columns[i] = column;
- }
-
- Check.DebugAssert(targetTable is not null, "Target table should have a value");
-
- if (targetTable is TpcTablesExpression tpcTablesExpression)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnTPC(
- nameof(RelationalQueryableExtensions.ExecuteUpdate), tpcTablesExpression.EntityType.DisplayName()));
- return null;
- }
-
- // First, check if the provider has a native translation for the update represented by the select expression.
- // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).
- // Providers may override IsValidSelectExpressionForExecuteUpdate to add support for more cases via provider-specific UPDATE syntax.
- var selectExpression = (SelectExpression)source.QueryExpression;
- return IsValidSelectExpressionForExecuteUpdate(selectExpression, targetTable, out var tableExpression)
- ? TranslateValueExpressions(this, source, selectExpression, tableExpression, propertyValueLambdaExpressions, columns)
- : PushdownWithPkInnerJoinPredicate();
-
- void PopulateSetPropertyCalls(
- Expression expression,
- List<(LambdaExpression, Expression)> list,
- ParameterExpression parameter)
- {
- switch (expression)
- {
- case ParameterExpression p
- when parameter == p:
- break;
-
- case MethodCallExpression
- {
- Method:
- {
- IsGenericMethod: true,
- Name: nameof(SetPropertyCalls.SetProperty),
- DeclaringType.IsGenericType: true
- }
- } methodCallExpression
- when methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(SetPropertyCalls<>):
- list.Add(((LambdaExpression)methodCallExpression.Arguments[0], methodCallExpression.Arguments[1]));
-
- PopulateSetPropertyCalls(methodCallExpression.Object!, list, parameter);
-
- break;
-
- default:
- AddTranslationErrorDetails(RelationalStrings.InvalidArgumentToExecuteUpdate);
- break;
- }
- }
-
- static NonQueryExpression? TranslateValueExpressions(
- RelationalQueryableMethodTranslatingExpressionVisitor visitor,
- ShapedQueryExpression source,
- SelectExpression selectExpression,
- TableExpression tableExpression,
- List<(LambdaExpression PropertySelector, Expression ValueExpression)> propertyValueLambdaExpression,
- ColumnExpression[] columns)
- {
- var setters = new ColumnValueSetter[columns.Length];
-
- for (var i = 0; i < propertyValueLambdaExpression.Count; i++)
- {
- var column = columns[i];
- var (_, valueSelector) = propertyValueLambdaExpression[i];
-
- var remappedValueSelector = valueSelector is LambdaExpression lambdaExpression
- ? visitor.RemapLambdaBody(source, lambdaExpression)
- : valueSelector;
-
- if (remappedValueSelector.Type != column.Type)
- {
- remappedValueSelector = Expression.Convert(remappedValueSelector, column.Type);
- }
-
- if (visitor.TranslateExpression(remappedValueSelector, applyDefaultTypeMapping: false)
- is not SqlExpression translatedValueSelector)
- {
- visitor.AddTranslationErrorDetails(RelationalStrings.InvalidValueInSetProperty(valueSelector.Print()));
- return null;
- }
-
- // Apply the type mapping of the column (translated from the property selector above) to the value,
- // and apply alias uniquification to it.
- translatedValueSelector = visitor._sqlExpressionFactory.ApplyTypeMapping(translatedValueSelector, column.TypeMapping);
- translatedValueSelector = selectExpression.AssignUniqueAliases(translatedValueSelector);
-
- setters[i] = new ColumnValueSetter(column, translatedValueSelector);
- }
-
- selectExpression.ReplaceProjection(new List());
- selectExpression.ApplyProjection();
-
- return new NonQueryExpression(new UpdateExpression(tableExpression, selectExpression, setters));
- }
-
- NonQueryExpression? PushdownWithPkInnerJoinPredicate()
- {
- // The provider doesn't natively support the update.
- // As a fallback, we place the original query in a subquery and user an INNER JOIN on the primary key columns.
-
- // Note that unlike with ExecuteDelete, we cannot use a Contains subquery (which would produce the simpler
- // WHERE Id IN (SELECT ...) syntax), since we allow projecting out to arbitrary shapes (e.g. anonymous types) before the
- // ExecuteUpdate.
-
- // To rewrite the query, we need to know the primary key properties, which requires getting the entity type.
- // Although there may be several entity types involved, we've already verified that they all map to the same table.
- // Since we don't support table sharing of multiple entity types with different keys, simply get the entity type and key from
- // the first property selector.
-
- // The following mechanism for extracting the entity type from property selectors only supports simple member access,
- // EF.Function, etc. We also unwrap casts to interface/base class (#29618). Note that owned IncludeExpressions have already
- // been pruned from the source before remapping the lambda (#28727).
-
- var firstPropertySelector = propertyValueLambdaExpressions[0].PropertySelector;
- var shaper = RemapLambdaBody(source, firstPropertySelector).UnwrapTypeConversion(out _) switch
- {
- MemberExpression { Expression : not null } memberExpression
- when memberExpression.Expression.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
- => s,
-
- MethodCallExpression mce when mce.TryGetEFPropertyArguments(out var source, out _)
- && source.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
- => s,
-
- MethodCallExpression mce when mce.TryGetIndexerArguments(RelationalDependencies.Model, out var source2, out _)
- && source2.UnwrapTypeConversion(out _) is StructuralTypeShaperExpression s
- => s,
-
- _ => null
- };
-
- if (shaper is null)
- {
- AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(firstPropertySelector));
- return null;
- }
-
- if (shaper.StructuralType is not IEntityType entityType)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteUpdateSubqueryNotSupportedOverComplexTypes(shaper.StructuralType.DisplayName()));
- return null;
- }
-
- if (entityType.FindPrimaryKey() is not IKey pk)
- {
- AddTranslationErrorDetails(
- RelationalStrings.ExecuteOperationOnKeylessEntityTypeWithUnsupportedOperator(
- nameof(RelationalQueryableExtensions.ExecuteUpdate),
- entityType.DisplayName()));
- return null;
- }
-
- // Generate the INNER JOIN around the original query, on the PK properties.
- var outer = (ShapedQueryExpression)Visit(new EntityQueryRootExpression(entityType));
- var inner = source;
- var outerParameter = Expression.Parameter(entityType.ClrType);
- var outerKeySelector = Expression.Lambda(outerParameter.CreateKeyValuesExpression(pk.Properties), outerParameter);
- var firstPropertyLambdaExpression = propertyValueLambdaExpressions[0].Item1;
- var entitySource = GetEntitySource(RelationalDependencies.Model, firstPropertyLambdaExpression.Body);
- var innerKeySelector = Expression.Lambda(
- entitySource.CreateKeyValuesExpression(pk.Properties), firstPropertyLambdaExpression.Parameters);
-
- var joinPredicate = CreateJoinPredicate(outer, outerKeySelector, inner, innerKeySelector);
-
- Check.DebugAssert(joinPredicate != null, "Join predicate shouldn't be null");
-
- var outerSelectExpression = (SelectExpression)outer.QueryExpression;
- var outerShaperExpression = outerSelectExpression.AddInnerJoin(inner, joinPredicate, outer.ShaperExpression);
- outer = outer.UpdateShaperExpression(outerShaperExpression);
- var transparentIdentifierType = outer.ShaperExpression.Type;
- var transparentIdentifierParameter = Expression.Parameter(transparentIdentifierType);
-
- var propertyReplacement = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Outer");
- var valueReplacement = AccessField(transparentIdentifierType, transparentIdentifierParameter, "Inner");
- for (var i = 0; i < propertyValueLambdaExpressions.Count; i++)
- {
- var (propertyExpression, valueExpression) = propertyValueLambdaExpressions[i];
- propertyExpression = Expression.Lambda(
- ReplacingExpressionVisitor.Replace(
- ReplacingExpressionVisitor.Replace(
- firstPropertyLambdaExpression.Parameters[0],
- propertyExpression.Parameters[0],
- entitySource),
- propertyReplacement, propertyExpression.Body),
- transparentIdentifierParameter);
-
- valueExpression = valueExpression is LambdaExpression lambdaExpression
- ? Expression.Lambda(
- ReplacingExpressionVisitor.Replace(lambdaExpression.Parameters[0], valueReplacement, lambdaExpression.Body),
- transparentIdentifierParameter)
- : valueExpression;
-
- propertyValueLambdaExpressions[i] = (propertyExpression, valueExpression);
- }
-
- tableExpression = (TableExpression)outerSelectExpression.Tables[0];
-
- // Re-translate the property selectors to get column expressions pointing to the new outer select expression (the original one
- // has been pushed down into a subquery).
- for (var i = 0; i < propertyValueLambdaExpressions.Count; i++)
- {
- var (propertySelector, _) = propertyValueLambdaExpressions[i];
- var propertySelectorBody = RemapLambdaBody(outer, propertySelector).UnwrapTypeConversion(out _);
-
- if (TranslateExpression(propertySelectorBody) is not ColumnExpression column)
- {
- AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(propertySelector.Print()));
- return null;
- }
-
- columns[i] = column;
- }
-
- return TranslateValueExpressions(this, outer, outerSelectExpression, tableExpression, propertyValueLambdaExpressions, columns);
- }
-
- static Expression GetEntitySource(IModel model, Expression propertyAccessExpression)
- {
- propertyAccessExpression = propertyAccessExpression.UnwrapTypeConversion(out _);
- if (propertyAccessExpression is MethodCallExpression mce)
- {
- if (mce.TryGetEFPropertyArguments(out var source, out _))
- {
- return source;
- }
-
- if (mce.TryGetIndexerArguments(model, out var source2, out _))
- {
- return source2;
- }
- }
-
- return ((MemberExpression)propertyAccessExpression).Expression!;
- }
- }
-
- ///
- /// Checks weather the current select expression can be used as-is for executing a delete operation, or whether it must be pushed
- /// down into a subquery.
- ///
- ///
- ///
- /// By default, only single-table select expressions are supported, and optionally with a predicate.
- ///
- ///
- /// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
- /// When doing this, VisitDelete must also be overridden in the provider's QuerySqlGenerator to add SQL generation support for
- /// the feature.
- ///
- ///
- /// The select expression to validate.
- /// The structural type shaper expression on which the delete operation is being applied.
- /// The table expression from which rows are being deleted.
- ///
- /// Returns if the current select expression can be used for delete as-is, otherwise.
- ///
- protected virtual bool IsValidSelectExpressionForExecuteDelete(
- SelectExpression selectExpression,
- StructuralTypeShaperExpression shaper,
- [NotNullWhen(true)] out TableExpression? tableExpression)
- {
- if (selectExpression is
- {
- Tables: [TableExpression expression],
- Orderings: [],
- Offset: null,
- Limit: null,
- GroupBy: [],
- Having: null
- })
- {
- tableExpression = expression;
-
- return true;
- }
-
- tableExpression = null;
- return false;
- }
-
- ///
- /// Validates if the current select expression can be used for execute update operation or it requires to be joined as a subquery.
- ///
- ///
- ///
- /// By default, only multi-table select expressions are supported, and optionally with a predicate.
- ///
- ///
- /// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
- /// When doing this, VisitUpdate must also be overridden in the provider's QuerySqlGenerator to add SQL generation support for
- /// the feature.
- ///
- ///
- /// The select expression to validate.
- /// The target table containing the rows to be updated.
- ///
- /// The table expression corresponding to the provided , containing the rows to be updated.
- ///
- ///
- /// Returns if the current select expression can be used for update as-is, otherwise.
- ///
- protected virtual bool IsValidSelectExpressionForExecuteUpdate(
- SelectExpression selectExpression,
- TableExpressionBase targetTable,
- [NotNullWhen(true)] out TableExpression? tableExpression)
- {
- tableExpression = null;
- if (selectExpression is
- {
- Offset: null,
- Limit: null,
- IsDistinct: false,
- GroupBy: [],
- Having: null,
- Orderings: [],
- Tables.Count: > 0
- })
- {
- if (selectExpression.Tables.Count > 1)
- {
- // If the table we are looking for is the first table, then we need to verify whether we can lift the next table in FROM
- // clause
- if (ReferenceEquals(selectExpression.Tables[0], targetTable)
- && selectExpression.Tables[1] is not InnerJoinExpression and not CrossJoinExpression)
- {
- return false;
- }
-
- if (targetTable is JoinExpressionBase joinExpressionBase)
- {
- targetTable = joinExpressionBase.Table;
- }
- }
-
- if (targetTable is TableExpression te)
- {
- tableExpression = te;
- return true;
- }
- }
-
- return false;
- }
-
///
/// Translates the given expression into equivalent SQL representation.
///
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
index 45e2a1e1f6e..0296f6c1168 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/ComplexTypeBulkUpdatesTestBase.cs
@@ -106,6 +106,121 @@ public virtual Task Update_projected_complex_type_via_OrderBy_Skip_throws(bool a
s => s.SetProperty(c => c.ZipCode, 12345),
rowsAffectedCount: 3));
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_complex_type_to_parameter(bool async)
+ {
+ var newAddress = new Address
+ {
+ AddressLine1 = "New AddressLine1",
+ AddressLine2 = "New AddressLine2",
+ ZipCode = 99999,
+ Country = new()
+ {
+ Code = "FR",
+ FullName = "France"
+ },
+ Tags = new List { "new_tag1", "new_tag2" }
+ };
+
+ return AssertUpdate(
+ async,
+ ss => ss.Set(),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress, newAddress),
+ rowsAffectedCount: 3);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_nested_complex_type_to_parameter(bool async)
+ {
+ var newCountry = new Country
+ {
+ Code = "FR",
+ FullName = "France"
+ };
+
+ return AssertUpdate(
+ async,
+ ss => ss.Set(),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress.Country, newCountry),
+ rowsAffectedCount: 3);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_complex_type_to_another_database_complex_type(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set(),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress, x => x.BillingAddress),
+ rowsAffectedCount: 3);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_complex_type_to_inline_without_lambda(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set(),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress, new Address
+ {
+ AddressLine1 = "New AddressLine1",
+ AddressLine2 = "New AddressLine2",
+ ZipCode = 99999,
+ Country = new()
+ {
+ Code = "FR",
+ FullName = "France"
+ },
+ Tags = new List { "new_tag1", "new_tag2" }
+ }),
+ rowsAffectedCount: 3);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_complex_type_to_inline_with_lambda(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set(),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress, x => new Address
+ {
+ AddressLine1 = "New AddressLine1",
+ AddressLine2 = "New AddressLine2",
+ ZipCode = 99999,
+ Country = new()
+ {
+ Code = "FR",
+ FullName = "France"
+ },
+ Tags = new List { "new_tag1", "new_tag2" }
+ }),
+ rowsAffectedCount: 3);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_complex_type_to_another_database_complex_type_with_subquery(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().OrderBy(c => c.Id).Skip(1),
+ c => c,
+ s => s.SetProperty(x => x.ShippingAddress, x => x.BillingAddress),
+ rowsAffectedCount: 2);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_collection_inside_complex_type(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set(),
+ 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/TestUtilities/TestSqlLoggerFactory.cs b/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs
index bf57b50d51e..12eb14f7557 100644
--- a/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs
+++ b/test/EFCore.Relational.Specification.Tests/TestUtilities/TestSqlLoggerFactory.cs
@@ -254,7 +254,7 @@ void RewriteSourceWithNewBaseline(string fileName, int lineNumber)
indentBuilder.Append(" ");
var indent = indentBuilder.ToString();
var newBaseLine = $@"Assert{(forUpdate ? "ExecuteUpdate" : "")}Sql(
-{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})";
+{string.Join("," + Environment.NewLine + indent + "//" + Environment.NewLine, SqlStatements.Skip(offset).Take(count).Select(sql => indent + "\"\"\"" + Environment.NewLine + sql + Environment.NewLine + "\"\"\""))})";
var numNewlinesInRewritten = newBaseLine.Count(c => c is '\n' or '\r');
writer.Write(newBaseLine);
diff --git a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs
index ceb4a5d65af..c91a7ae046b 100644
--- a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs
@@ -143,8 +143,15 @@ public virtual Task Complex_type_equals_constant(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
- Country = new Country { FullName = "United States", Code = "US" }
- }));
+ Country = new Country { FullName = "United States", Code = "US" },
+ Tags = new List { "foo", "bar" }
+ }),
+ ss => ss.Set().Where(
+ c =>
+ c.ShippingAddress.AddressLine1 == "804 S. Lakeshore Road"
+ && c.ShippingAddress.ZipCode == 38654
+ && c.ShippingAddress.Country == new Country { FullName = "United States", Code = "US" }
+ && c.ShippingAddress.Tags.SequenceEqual(new List { "foo", "bar" })));
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
@@ -154,12 +161,19 @@ public virtual Task Complex_type_equals_parameter(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
- Country = new Country { FullName = "United States", Code = "US" }
+ Country = new Country { FullName = "United States", Code = "US" },
+ Tags = new List { "foo", "bar" }
};
return AssertQuery(
async,
- ss => ss.Set().Where(c => c.ShippingAddress == address));
+ ss => ss.Set().Where(c => c.ShippingAddress == address),
+ ss => ss.Set().Where(
+ c =>
+ c.ShippingAddress.AddressLine1 == "804 S. Lakeshore Road"
+ && c.ShippingAddress.ZipCode == 38654
+ && c.ShippingAddress.Country == new Country { FullName = "United States", Code = "US" }
+ && c.ShippingAddress.Tags.SequenceEqual(new List { "foo", "bar" })));
}
[ConditionalTheory]
@@ -194,13 +208,21 @@ public virtual Task Contains_over_complex_type(bool async)
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
- Country = new Country { FullName = "United States", Code = "US" }
+ Country = new Country { FullName = "United States", Code = "US" },
+ Tags = new List { "foo", "bar" }
};
return AssertQuery(
async,
ss => ss.Set().Where(
- c => ss.Set().Select(c => c.ShippingAddress).Contains(address)));
+ c => ss.Set().Select(c => c.ShippingAddress).Contains(address)),
+ ss => ss.Set().Where(
+ c => ss.Set().Select(c => c.ShippingAddress).Any(
+ a =>
+ a.AddressLine1 == "804 S. Lakeshore Road"
+ && a.ZipCode == 38654
+ && a.Country == new Country { FullName = "United States", Code = "US" }
+ && a.Tags.SequenceEqual(new List { "foo", "bar" }))));
}
[ConditionalTheory]
diff --git a/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/ComplexTypeData.cs b/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/ComplexTypeData.cs
index cbba0e79225..e10e3c5a721 100644
--- a/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/ComplexTypeData.cs
+++ b/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/ComplexTypeData.cs
@@ -52,7 +52,8 @@ private static IReadOnlyList CreateCustomers()
{
AddressLine1 = "804 S. Lakeshore Road",
ZipCode = 38654,
- Country = new Country { FullName = "United States", Code = "US" }
+ Country = new Country { FullName = "United States", Code = "US" },
+ Tags = new List { "foo", "bar" }
};
var customer1 = new Customer
@@ -71,13 +72,15 @@ private static IReadOnlyList CreateCustomers()
{
AddressLine1 = "72 Hickory Rd.",
ZipCode = 07728,
- Country = new Country { FullName = "Germany", Code = "DE" }
+ Country = new Country { FullName = "Germany", Code = "DE" },
+ Tags = new List { "baz" }
},
BillingAddress = new Address
{
AddressLine1 = "79 Main St.",
ZipCode = 29293,
- Country = new Country { FullName = "Germany", Code = "DE" }
+ Country = new Country { FullName = "Germany", Code = "DE" },
+ Tags = new List { "a1", "a2", "a3" }
}
};
@@ -85,7 +88,8 @@ private static IReadOnlyList CreateCustomers()
{
AddressLine1 = "79 Main St.",
ZipCode = 29293,
- Country = new Country { FullName = "Germany", Code = "DE" }
+ Country = new Country { FullName = "Germany", Code = "DE" },
+ Tags = new List { "foo", "moo" }
};
var customer3 = new Customer
diff --git a/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/Model.cs b/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/Model.cs
index 689bc1cdafa..a35c5782bd2 100644
--- a/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/Model.cs
+++ b/test/EFCore.Specification.Tests/TestModels/ComplexTypeModel/Model.cs
@@ -19,6 +19,7 @@ public record Address
public required string AddressLine1 { get; set; }
public string? AddressLine2 { get; set; }
public int ZipCode { get; set; }
+ public List Tags { get; set; } = new();
public required Country Country { get; set; }
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
index 1d6aab91a85..d6d4ffd52da 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/ComplexTypeBulkUpdatesSqlServerTest.cs
@@ -103,6 +103,134 @@ public override async Task Update_projected_complex_type_via_OrderBy_Skip_throws
AssertExecuteUpdateSql();
}
+ public override async Task Update_complex_type_to_parameter(bool async)
+ {
+ await base.Update_complex_type_to_parameter(async);
+
+ AssertExecuteUpdateSql(
+ """
+@__complex_type_newAddress_0_AddressLine1='New AddressLine1' (Size = 4000)
+@__complex_type_newAddress_0_AddressLine2='New AddressLine2' (Size = 4000)
+@__complex_type_newAddress_0_Tags='["new_tag1","new_tag2"]' (Size = 4000)
+@__complex_type_newAddress_0_ZipCode='99999' (Nullable = true)
+@__complex_type_newAddress_0_Code='FR' (Size = 4000)
+@__complex_type_newAddress_0_FullName='France' (Size = 4000)
+
+UPDATE [c]
+SET [c].[ShippingAddress_AddressLine1] = @__complex_type_newAddress_0_AddressLine1,
+ [c].[ShippingAddress_AddressLine2] = @__complex_type_newAddress_0_AddressLine2,
+ [c].[ShippingAddress_Tags] = @__complex_type_newAddress_0_Tags,
+ [c].[ShippingAddress_ZipCode] = @__complex_type_newAddress_0_ZipCode,
+ [c].[ShippingAddress_Country_Code] = @__complex_type_newAddress_0_Code,
+ [c].[ShippingAddress_Country_FullName] = @__complex_type_newAddress_0_FullName
+FROM [Customer] AS [c]
+""");
+ }
+
+ public override async Task Update_nested_complex_type_to_parameter(bool async)
+ {
+ await base.Update_nested_complex_type_to_parameter(async);
+
+ AssertExecuteUpdateSql(
+ """
+@__complex_type_newCountry_0_Code='FR' (Size = 4000)
+@__complex_type_newCountry_0_FullName='France' (Size = 4000)
+
+UPDATE [c]
+SET [c].[ShippingAddress_Country_Code] = @__complex_type_newCountry_0_Code,
+ [c].[ShippingAddress_Country_FullName] = @__complex_type_newCountry_0_FullName
+FROM [Customer] AS [c]
+""");
+ }
+
+ public override async Task Update_complex_type_to_another_database_complex_type(bool async)
+ {
+ await base.Update_complex_type_to_another_database_complex_type(async);
+
+ AssertExecuteUpdateSql(
+ """
+UPDATE [c]
+SET [c].[ShippingAddress_AddressLine1] = [c].[BillingAddress_AddressLine1],
+ [c].[ShippingAddress_AddressLine2] = [c].[BillingAddress_AddressLine2],
+ [c].[ShippingAddress_Tags] = [c].[BillingAddress_Tags],
+ [c].[ShippingAddress_ZipCode] = [c].[BillingAddress_ZipCode],
+ [c].[ShippingAddress_Country_Code] = [c].[ShippingAddress_Country_Code],
+ [c].[ShippingAddress_Country_FullName] = [c].[ShippingAddress_Country_FullName]
+FROM [Customer] AS [c]
+""");
+ }
+
+ public override async Task Update_complex_type_to_inline_without_lambda(bool async)
+ {
+ await base.Update_complex_type_to_inline_without_lambda(async);
+
+ AssertExecuteUpdateSql(
+ """
+UPDATE [c]
+SET [c].[ShippingAddress_AddressLine1] = N'New AddressLine1',
+ [c].[ShippingAddress_AddressLine2] = N'New AddressLine2',
+ [c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]',
+ [c].[ShippingAddress_ZipCode] = 99999,
+ [c].[ShippingAddress_Country_Code] = N'FR',
+ [c].[ShippingAddress_Country_FullName] = N'France'
+FROM [Customer] AS [c]
+""");
+ }
+
+ public override async Task Update_complex_type_to_inline_with_lambda(bool async)
+ {
+ await base.Update_complex_type_to_inline_with_lambda(async);
+
+ AssertExecuteUpdateSql(
+ """
+UPDATE [c]
+SET [c].[ShippingAddress_AddressLine1] = N'New AddressLine1',
+ [c].[ShippingAddress_AddressLine2] = N'New AddressLine2',
+ [c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]',
+ [c].[ShippingAddress_ZipCode] = 99999,
+ [c].[ShippingAddress_Country_Code] = N'FR',
+ [c].[ShippingAddress_Country_FullName] = N'France'
+FROM [Customer] AS [c]
+""");
+ }
+
+ public override async Task Update_complex_type_to_another_database_complex_type_with_subquery(bool async)
+ {
+ await base.Update_complex_type_to_another_database_complex_type_with_subquery(async);
+
+ AssertExecuteUpdateSql(
+ """
+@__p_0='1'
+
+UPDATE [c]
+SET [c].[ShippingAddress_AddressLine1] = [t].[BillingAddress_AddressLine1],
+ [c].[ShippingAddress_AddressLine2] = [t].[BillingAddress_AddressLine2],
+ [c].[ShippingAddress_Tags] = [t].[BillingAddress_Tags],
+ [c].[ShippingAddress_ZipCode] = [t].[BillingAddress_ZipCode],
+ [c].[ShippingAddress_Country_Code] = [t].[ShippingAddress_Country_Code],
+ [c].[ShippingAddress_Country_FullName] = [t].[ShippingAddress_Country_FullName]
+FROM [Customer] AS [c]
+INNER JOIN (
+ SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+ FROM [Customer] AS [c0]
+ ORDER BY [c0].[Id]
+ OFFSET @__p_0 ROWS
+) AS [t] ON [c].[Id] = [t].[Id]
+""");
+ }
+
+ public override async Task Update_collection_inside_complex_type(bool async)
+ {
+ await base.Update_collection_inside_complex_type(async);
+
+ AssertExecuteUpdateSql(
+ """
+UPDATE [c]
+SET [c].[ShippingAddress_Tags] = N'["new_tag1","new_tag2"]'
+FROM [Customer] AS [c]
+""");
+ }
+
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs
index 52ed43e3ea5..c106961995b 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs
@@ -19,7 +19,7 @@ public override async Task Filter_on_property_inside_complex_type(bool async)
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[ShippingAddress_ZipCode] = 7728
""");
@@ -31,7 +31,7 @@ public override async Task Filter_on_property_inside_nested_complex_type(bool as
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[ShippingAddress_Country_Code] = N'DE'
""");
@@ -45,9 +45,9 @@ public override async Task Filter_on_property_inside_complex_type_after_subquery
"""
@__p_0='1'
-SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
+SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_Tags], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_Tags], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
FROM (
- SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+ SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
ORDER BY [c].[Id]
OFFSET @__p_0 ROWS
@@ -64,9 +64,9 @@ public override async Task Filter_on_property_inside_nested_complex_type_after_s
"""
@__p_0='1'
-SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
+SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_Tags], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_Tags], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
FROM (
- SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+ SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
ORDER BY [c].[Id]
OFFSET @__p_0 ROWS
@@ -81,7 +81,7 @@ public override async Task Filter_on_required_property_inside_required_complex_t
AssertSql(
"""
-SELECT [c].[Id], [c].[OptionalCustomerId], [c].[RequiredCustomerId], [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName], [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[OptionalCustomerId], [c].[RequiredCustomerId], [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName], [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_Tags], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName]
FROM [CustomerGroup] AS [c]
LEFT JOIN [Customer] AS [c0] ON [c].[OptionalCustomerId] = [c0].[Id]
INNER JOIN [Customer] AS [c1] ON [c].[RequiredCustomerId] = [c1].[Id]
@@ -95,7 +95,7 @@ public override async Task Filter_on_required_property_inside_required_complex_t
AssertSql(
"""
-SELECT [c].[Id], [c].[OptionalCustomerId], [c].[RequiredCustomerId], [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName], [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[OptionalCustomerId], [c].[RequiredCustomerId], [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_Tags], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName], [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [CustomerGroup] AS [c]
INNER JOIN [Customer] AS [c0] ON [c].[RequiredCustomerId] = [c0].[Id]
LEFT JOIN [Customer] AS [c1] ON [c].[OptionalCustomerId] = [c1].[Id]
@@ -119,7 +119,7 @@ public override async Task Project_complex_type_via_required_navigation(bool asy
AssertSql(
"""
-SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [CustomerGroup] AS [c]
INNER JOIN [Customer] AS [c0] ON [c].[RequiredCustomerId] = [c0].[Id]
""");
@@ -133,9 +133,9 @@ public override async Task Load_complex_type_after_subquery_on_entity_type(bool
"""
@__p_0='1'
-SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
+SELECT DISTINCT [t].[Id], [t].[Name], [t].[BillingAddress_AddressLine1], [t].[BillingAddress_AddressLine2], [t].[BillingAddress_Tags], [t].[BillingAddress_ZipCode], [t].[BillingAddress_Country_Code], [t].[BillingAddress_Country_FullName], [t].[ShippingAddress_AddressLine1], [t].[ShippingAddress_AddressLine2], [t].[ShippingAddress_Tags], [t].[ShippingAddress_ZipCode], [t].[ShippingAddress_Country_Code], [t].[ShippingAddress_Country_FullName]
FROM (
- SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+ SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
ORDER BY [c].[Id]
OFFSET @__p_0 ROWS
@@ -149,7 +149,7 @@ public override async Task Select_complex_type(bool async)
AssertSql(
"""
-SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
""");
}
@@ -182,7 +182,7 @@ public override async Task Select_complex_type_Where(bool async)
AssertSql(
"""
-SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[ShippingAddress_ZipCode] = 7728
""");
@@ -194,7 +194,7 @@ public override async Task Select_complex_type_Distinct(bool async)
AssertSql(
"""
-SELECT DISTINCT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT DISTINCT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
""");
}
@@ -205,9 +205,9 @@ public override async Task Complex_type_equals_complex_type(bool async)
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
-WHERE [c].[ShippingAddress_AddressLine1] = [c].[BillingAddress_AddressLine1] AND ([c].[ShippingAddress_AddressLine2] = [c].[BillingAddress_AddressLine2] OR ([c].[ShippingAddress_AddressLine2] IS NULL AND [c].[BillingAddress_AddressLine2] IS NULL)) AND [c].[ShippingAddress_ZipCode] = [c].[BillingAddress_ZipCode]
+WHERE [c].[ShippingAddress_AddressLine1] = [c].[BillingAddress_AddressLine1] AND ([c].[ShippingAddress_AddressLine2] = [c].[BillingAddress_AddressLine2] OR ([c].[ShippingAddress_AddressLine2] IS NULL AND [c].[BillingAddress_AddressLine2] IS NULL)) AND [c].[ShippingAddress_Tags] = [c].[BillingAddress_Tags] AND [c].[ShippingAddress_ZipCode] = [c].[BillingAddress_ZipCode]
""");
}
@@ -217,9 +217,9 @@ public override async Task Complex_type_equals_constant(bool async)
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
-WHERE [c].[ShippingAddress_AddressLine1] = N'804 S. Lakeshore Road' AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_ZipCode] = 38654 AND [c].[ShippingAddress_Country_Code] = N'US' AND [c].[ShippingAddress_Country_FullName] = N'United States'
+WHERE [c].[ShippingAddress_AddressLine1] = N'804 S. Lakeshore Road' AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_Tags] = N'["foo","bar"]' AND [c].[ShippingAddress_ZipCode] = 38654 AND [c].[ShippingAddress_Country_Code] = N'US' AND [c].[ShippingAddress_Country_FullName] = N'United States'
""");
}
@@ -230,13 +230,14 @@ public override async Task Complex_type_equals_parameter(bool async)
AssertSql(
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
+@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
-WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName
+WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName
""");
}
@@ -261,16 +262,17 @@ public override async Task Contains_over_complex_type(bool async)
AssertSql(
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
+@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE EXISTS (
SELECT 1
FROM [Customer] AS [c0]
- WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL AND [c0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName)
+ WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL AND [c0].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName)
""");
}
@@ -280,11 +282,11 @@ public override async Task Concat_complex_type(bool async)
AssertSql(
"""
-SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[Id] = 1
UNION ALL
-SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c0]
WHERE [c0].[Id] = 2
""");
@@ -296,11 +298,11 @@ public override async Task Concat_entity_type_containing_complex_property(bool a
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[Id] = 1
UNION ALL
-SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c0]
WHERE [c0].[Id] = 2
""");
@@ -312,11 +314,11 @@ public override async Task Union_entity_type_containing_complex_property(bool as
AssertSql(
"""
-SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[Id] = 1
UNION
-SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c0]
WHERE [c0].[Id] = 2
""");
@@ -328,11 +330,11 @@ public override async Task Union_complex_type(bool async)
AssertSql(
"""
-SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
+SELECT [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[Id] = 1
UNION
-SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
+SELECT [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c0]
WHERE [c0].[Id] = 2
""");
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs
index d3423b9e7d0..3dd4ff5cec3 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs
@@ -19,7 +19,7 @@ public override async Task Filter_on_property_inside_complex_type(bool async)
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."ShippingAddress_ZipCode" = 7728
""");
@@ -31,7 +31,7 @@ public override async Task Filter_on_property_inside_nested_complex_type(bool as
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."ShippingAddress_Country_Code" = 'DE'
""");
@@ -45,9 +45,9 @@ public override async Task Filter_on_property_inside_complex_type_after_subquery
"""
@__p_0='1'
-SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
+SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_Tags", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_Tags", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
FROM (
- SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+ SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
ORDER BY "c"."Id"
LIMIT -1 OFFSET @__p_0
@@ -64,9 +64,9 @@ public override async Task Filter_on_property_inside_nested_complex_type_after_s
"""
@__p_0='1'
-SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
+SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_Tags", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_Tags", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
FROM (
- SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+ SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
ORDER BY "c"."Id"
LIMIT -1 OFFSET @__p_0
@@ -81,7 +81,7 @@ public override async Task Filter_on_required_property_inside_required_complex_t
AssertSql(
"""
-SELECT "c"."Id", "c"."OptionalCustomerId", "c"."RequiredCustomerId", "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName", "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."OptionalCustomerId", "c"."RequiredCustomerId", "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_Tags", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName", "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_Tags", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_Tags", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName"
FROM "CustomerGroup" AS "c"
LEFT JOIN "Customer" AS "c0" ON "c"."OptionalCustomerId" = "c0"."Id"
INNER JOIN "Customer" AS "c1" ON "c"."RequiredCustomerId" = "c1"."Id"
@@ -95,7 +95,7 @@ public override async Task Filter_on_required_property_inside_required_complex_t
AssertSql(
"""
-SELECT "c"."Id", "c"."OptionalCustomerId", "c"."RequiredCustomerId", "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName", "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."OptionalCustomerId", "c"."RequiredCustomerId", "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_Tags", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_Tags", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName", "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_Tags", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "CustomerGroup" AS "c"
INNER JOIN "Customer" AS "c0" ON "c"."RequiredCustomerId" = "c0"."Id"
LEFT JOIN "Customer" AS "c1" ON "c"."OptionalCustomerId" = "c1"."Id"
@@ -119,7 +119,7 @@ public override async Task Project_complex_type_via_required_navigation(bool asy
AssertSql(
"""
-SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "CustomerGroup" AS "c"
INNER JOIN "Customer" AS "c0" ON "c"."RequiredCustomerId" = "c0"."Id"
""");
@@ -133,9 +133,9 @@ public override async Task Load_complex_type_after_subquery_on_entity_type(bool
"""
@__p_0='1'
-SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
+SELECT DISTINCT "t"."Id", "t"."Name", "t"."BillingAddress_AddressLine1", "t"."BillingAddress_AddressLine2", "t"."BillingAddress_Tags", "t"."BillingAddress_ZipCode", "t"."BillingAddress_Country_Code", "t"."BillingAddress_Country_FullName", "t"."ShippingAddress_AddressLine1", "t"."ShippingAddress_AddressLine2", "t"."ShippingAddress_Tags", "t"."ShippingAddress_ZipCode", "t"."ShippingAddress_Country_Code", "t"."ShippingAddress_Country_FullName"
FROM (
- SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+ SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
ORDER BY "c"."Id"
LIMIT -1 OFFSET @__p_0
@@ -149,7 +149,7 @@ public override async Task Select_complex_type(bool async)
AssertSql(
"""
-SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
""");
}
@@ -182,7 +182,7 @@ public override async Task Select_complex_type_Where(bool async)
AssertSql(
"""
-SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."ShippingAddress_ZipCode" = 7728
""");
@@ -194,7 +194,7 @@ public override async Task Select_complex_type_Distinct(bool async)
AssertSql(
"""
-SELECT DISTINCT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT DISTINCT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
""");
}
@@ -205,9 +205,9 @@ public override async Task Complex_type_equals_complex_type(bool async)
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
-WHERE "c"."ShippingAddress_AddressLine1" = "c"."BillingAddress_AddressLine1" AND ("c"."ShippingAddress_AddressLine2" = "c"."BillingAddress_AddressLine2" OR ("c"."ShippingAddress_AddressLine2" IS NULL AND "c"."BillingAddress_AddressLine2" IS NULL)) AND "c"."ShippingAddress_ZipCode" = "c"."BillingAddress_ZipCode"
+WHERE "c"."ShippingAddress_AddressLine1" = "c"."BillingAddress_AddressLine1" AND ("c"."ShippingAddress_AddressLine2" = "c"."BillingAddress_AddressLine2" OR ("c"."ShippingAddress_AddressLine2" IS NULL AND "c"."BillingAddress_AddressLine2" IS NULL)) AND "c"."ShippingAddress_Tags" = "c"."BillingAddress_Tags" AND "c"."ShippingAddress_ZipCode" = "c"."BillingAddress_ZipCode"
""");
}
@@ -217,9 +217,9 @@ public override async Task Complex_type_equals_constant(bool async)
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
-WHERE "c"."ShippingAddress_AddressLine1" = '804 S. Lakeshore Road' AND "c"."ShippingAddress_AddressLine2" IS NULL AND "c"."ShippingAddress_ZipCode" = 38654 AND "c"."ShippingAddress_Country_Code" = 'US' AND "c"."ShippingAddress_Country_FullName" = 'United States'
+WHERE "c"."ShippingAddress_AddressLine1" = '804 S. Lakeshore Road' AND "c"."ShippingAddress_AddressLine2" IS NULL AND "c"."ShippingAddress_Tags" = '["foo","bar"]' AND "c"."ShippingAddress_ZipCode" = 38654 AND "c"."ShippingAddress_Country_Code" = 'US' AND "c"."ShippingAddress_Country_FullName" = 'United States'
""");
}
@@ -230,13 +230,14 @@ public override async Task Complex_type_equals_parameter(bool async)
AssertSql(
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 21)
+@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 13)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 2)
@__entity_equality_address_0_FullName='United States' (Size = 13)
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
-WHERE "c"."ShippingAddress_AddressLine1" = @__entity_equality_address_0_AddressLine1 AND "c"."ShippingAddress_AddressLine2" IS NULL AND "c"."ShippingAddress_ZipCode" = @__entity_equality_address_0_ZipCode AND "c"."ShippingAddress_Country_Code" = @__entity_equality_address_0_Code AND "c"."ShippingAddress_Country_FullName" = @__entity_equality_address_0_FullName
+WHERE "c"."ShippingAddress_AddressLine1" = @__entity_equality_address_0_AddressLine1 AND "c"."ShippingAddress_AddressLine2" IS NULL AND "c"."ShippingAddress_Tags" = @__entity_equality_address_0_Tags AND "c"."ShippingAddress_ZipCode" = @__entity_equality_address_0_ZipCode AND "c"."ShippingAddress_Country_Code" = @__entity_equality_address_0_Code AND "c"."ShippingAddress_Country_FullName" = @__entity_equality_address_0_FullName
""");
}
@@ -261,16 +262,17 @@ public override async Task Contains_over_complex_type(bool async)
AssertSql(
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 21)
+@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 13)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 2)
@__entity_equality_address_0_FullName='United States' (Size = 13)
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE EXISTS (
SELECT 1
FROM "Customer" AS "c0"
- WHERE "c0"."ShippingAddress_AddressLine1" = @__entity_equality_address_0_AddressLine1 AND "c0"."ShippingAddress_AddressLine2" IS NULL AND "c0"."ShippingAddress_ZipCode" = @__entity_equality_address_0_ZipCode AND "c0"."ShippingAddress_Country_Code" = @__entity_equality_address_0_Code AND "c0"."ShippingAddress_Country_FullName" = @__entity_equality_address_0_FullName)
+ WHERE "c0"."ShippingAddress_AddressLine1" = @__entity_equality_address_0_AddressLine1 AND "c0"."ShippingAddress_AddressLine2" IS NULL AND "c0"."ShippingAddress_Tags" = @__entity_equality_address_0_Tags AND "c0"."ShippingAddress_ZipCode" = @__entity_equality_address_0_ZipCode AND "c0"."ShippingAddress_Country_Code" = @__entity_equality_address_0_Code AND "c0"."ShippingAddress_Country_FullName" = @__entity_equality_address_0_FullName)
""");
}
@@ -280,11 +282,11 @@ public override async Task Concat_complex_type(bool async)
AssertSql(
"""
-SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."Id" = 1
UNION ALL
-SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c0"
WHERE "c0"."Id" = 2
""");
@@ -296,11 +298,11 @@ public override async Task Concat_entity_type_containing_complex_property(bool a
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."Id" = 1
UNION ALL
-SELECT "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_Tags", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c0"
WHERE "c0"."Id" = 2
""");
@@ -312,11 +314,11 @@ public override async Task Union_entity_type_containing_complex_property(bool as
AssertSql(
"""
-SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."Id" = 1
UNION
-SELECT "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c0"."Id", "c0"."Name", "c0"."BillingAddress_AddressLine1", "c0"."BillingAddress_AddressLine2", "c0"."BillingAddress_Tags", "c0"."BillingAddress_ZipCode", "c0"."BillingAddress_Country_Code", "c0"."BillingAddress_Country_FullName", "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c0"
WHERE "c0"."Id" = 2
""");
@@ -328,11 +330,11 @@ public override async Task Union_complex_type(bool async)
AssertSql(
"""
-SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
+SELECT "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c"
WHERE "c"."Id" = 1
UNION
-SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
+SELECT "c0"."ShippingAddress_AddressLine1", "c0"."ShippingAddress_AddressLine2", "c0"."ShippingAddress_Tags", "c0"."ShippingAddress_ZipCode", "c0"."ShippingAddress_Country_Code", "c0"."ShippingAddress_Country_FullName"
FROM "Customer" AS "c0"
WHERE "c0"."Id" = 2
""");