diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs index 53466ffa814..afa6941c44a 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs @@ -16,9 +16,28 @@ public class CosmosQuerySqlGenerator(ITypeMappingSource typeMappingSource) : Sql { private readonly IndentedStringBuilder _sqlBuilder = new(); private IReadOnlyDictionary _parameterValues = null!; + + /// + /// The Cosmos SqlParameters which will get sent in the CosmosQuery. + /// private List _sqlParameters = null!; + + /// + /// Lookup table from (the "original" name) to the Cosmos , + /// whose name has underdone uniquification and prefixing. This is used when the same parameter is referenced multiple times in the query. + /// + private Dictionary _sqlParametersByOriginalName = null!; + + /// + /// Contains final parameter names (prefixed, uniquified) seen so far, for uniquification purposes. + /// + private readonly HashSet _prefixedParameterNames = new(StringComparer.OrdinalIgnoreCase); + private ParameterNameGenerator _parameterNameGenerator = null!; + private static readonly bool UseOldBehavior37252 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37252", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -32,6 +51,7 @@ public virtual CosmosSqlQuery GetSqlQuery( _sqlBuilder.Clear(); _parameterValues = parameterValues; _sqlParameters = []; + _sqlParametersByOriginalName = []; _parameterNameGenerator = new ParameterNameGenerator(); Visit(selectExpression); @@ -401,7 +421,11 @@ when _parameterValues.TryGetValue(queryParameter.Name, out var parameterValue) substitutions = new string[parameterValues.Length]; for (var i = 0; i < parameterValues.Length; i++) { - var parameterName = _parameterNameGenerator.GenerateNext(); + // Note that we don't go through _sqlParametersByOriginalName, since the FromSql parameters we're adding here cannot + // be referenced multiple times. + var parameterName = UseOldBehavior37252 + ? _parameterNameGenerator.GenerateNext() + : PrefixAndUniquifyParameterName("p"); _sqlParameters.Add(new SqlParameter(parameterName, parameterValues[i])); substitutions[i] = parameterName; } @@ -677,20 +701,42 @@ protected override Expression VisitSqlConditional(SqlConditionalExpression sqlCo /// protected override Expression VisitSqlParameter(SqlParameterExpression sqlParameterExpression) { - var parameterName = $"@{sqlParameterExpression.Name}"; - - if (_sqlParameters.All(sp => sp.Name != parameterName)) + if (UseOldBehavior37252) { - Check.DebugAssert(sqlParameterExpression.TypeMapping is not null, "SqlParameterExpression without a type mapping."); + var parameterName = $"@{sqlParameterExpression.Name}"; + + if (_sqlParameters.All(sp => sp.Name != parameterName)) + { + Check.DebugAssert(sqlParameterExpression.TypeMapping is not null, "SqlParameterExpression without a type mapping."); + + _sqlParameters.Add( + new SqlParameter( + parameterName, + ((CosmosTypeMapping)sqlParameterExpression.TypeMapping) + .GenerateJToken(_parameterValues[sqlParameterExpression.Name]))); + } - _sqlParameters.Add( - new SqlParameter( - parameterName, - ((CosmosTypeMapping)sqlParameterExpression.TypeMapping) - .GenerateJToken(_parameterValues[sqlParameterExpression.Name]))); + _sqlBuilder.Append(parameterName); } + else + { + if (!_sqlParametersByOriginalName.TryGetValue(sqlParameterExpression.Name, out var sqlParameter)) + { + Check.DebugAssert(sqlParameterExpression.TypeMapping is not null, "SqlParameterExpression without a type mapping."); + + var parameterName = PrefixAndUniquifyParameterName(sqlParameterExpression.Name); - _sqlBuilder.Append(parameterName); + sqlParameter = new SqlParameter( + parameterName, + ((CosmosTypeMapping)sqlParameterExpression.TypeMapping) + .GenerateJToken(_parameterValues[sqlParameterExpression.Name])); + + _sqlParametersByOriginalName[sqlParameterExpression.Name] = sqlParameter; + _sqlParameters.Add(sqlParameter); + } + + _sqlBuilder.Append(sqlParameter.Name); + } return sqlParameterExpression; } @@ -863,6 +909,20 @@ private void GenerateFunction(string name, IReadOnlyList arguments) _sqlBuilder.Append(')'); } + private string PrefixAndUniquifyParameterName(string originalName) + { + var i = 0; + var prefixedName = $"@{originalName}"; + + while (_prefixedParameterNames.Contains(prefixedName)) + { + prefixedName = $"@{originalName + i++}"; + } + + _prefixedParameterNames.Add(prefixedName); + return prefixedName; + } + private sealed class ParameterNameGenerator { private int _count; diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 04a6940a970..d3bfe6092e3 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -644,8 +644,7 @@ protected virtual void GenerateComplexPropertyAnnotations( .Append('.') .Append("HasDiscriminator"); - if (discriminatorProperty.DeclaringType == property.ComplexType - && discriminatorProperty.Name != "Discriminator") + if (discriminatorProperty.DeclaringType == property.ComplexType) { var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType .MakeNullable(discriminatorProperty.IsNullable) @@ -953,8 +952,7 @@ protected virtual void GenerateEntityTypeAnnotations( .Append('.') .Append("HasDiscriminator"); - if (discriminatorProperty.DeclaringType == entityType - && discriminatorProperty.Name != "Discriminator") + if (discriminatorProperty.DeclaringType == entityType) { var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType .MakeNullable(discriminatorProperty.IsNullable) diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index b36f1622561..c687e81ec1f 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -2901,7 +2901,8 @@ public static Expression GenerateComplexPropertyShaperExpression( // We do not support complex type splitting, so we will only ever have a single table/view mapping to it. // See Issue #32853 and Issue #31248 - var complexTypeTable = complexType.GetViewOrTableMappings().Single().Table; + var viewOrTableTable = complexType.GetViewOrTableMappings().Single().Table; + var complexTypeTable = viewOrTableTable; if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out var tableAlias)) { @@ -2925,7 +2926,11 @@ public static Expression GenerateComplexPropertyShaperExpression( { var containerColumnName = complexType.GetContainerColumnName(); Check.DebugAssert(containerColumnName is not null, "Complex JSON type without a container column"); - var containerColumn = complexTypeTable.FindColumn(containerColumnName); + + // TODO: when the JSON column is on a FromSql() source, we use the default mappings from the relational model (see just above), + // but those don't yet contain complex types (#34627). We work around this here, get the column from the table mapping instead + // of from the default mapping. + var containerColumn = complexTypeTable.FindColumn(containerColumnName) ?? viewOrTableTable.FindColumn(containerColumnName); Check.DebugAssert(containerColumn is not null, "Complex JSON container table not found on relational table"); // If the source type is a JSON complex type; since we're binding over StructuralTypeProjectionExpression - which represents a relational diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs index c95af640f29..538d199b843 100644 --- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs +++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; @@ -31,6 +32,9 @@ public class SqlNullabilityProcessor : ExpressionVisitor /// private readonly Dictionary> _collectionParameterExpansionMap; + private static readonly bool UseOldBehavior37216 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37216", out var enabled) && enabled; + /// /// Creates a new instance of the class. /// @@ -188,6 +192,33 @@ protected override Expression VisitExtension(Expression node) throw new UnreachableException(); } + // We've inlined the user-provided collection from the values parameter into the SQL VALUES expression: (VALUES (1), (2)...). + // However, if the collection happens to be empty, this doesn't work as VALUES does not support empty sets. We convert it + // to a SELECT ... WHERE false to produce an empty result set instead. + if (!UseOldBehavior37216 && processedValues.Count == 0) + { + var select = new SelectExpression( + valuesExpression.Alias, + tables: [], + predicate: new SqlConstantExpression(false, Dependencies.TypeMappingSource.FindMapping(typeof(bool))), + groupBy: [], + having: null, + projections: valuesExpression.ColumnNames + .Select(n => new ProjectionExpression( + new SqlConstantExpression(value: null, elementTypeMapping.ClrType, elementTypeMapping), n)) + .ToList(), + distinct: false, + orderings: [], + offset: null, + limit: null, + tags: ReadOnlySet.Empty, + annotations: null, + sqlAliasManager: null!, + isMutable: false); + + return select; + } + return valuesExpression.Update(processedValues); } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 77cb0b5d732..6164e159e28 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -58,6 +58,9 @@ private static readonly PropertyInfo QueryContextContextPropertyInfo private readonly Dictionary _parameters = new(); + private static readonly bool UseOldBehavior37247 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37247", out var enabled) && enabled; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -1064,6 +1067,17 @@ private Expression ProcessExecuteUpdate(NavigationExpansionExpression source, Me { source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); + if (!UseOldBehavior37247) + { + // Apply any pending selector before processing the ExecuteUpdate setters; this adds a Select() (if necessary) before + // ExecuteUpdate, to avoid the pending selector flowing into each setter lambda and making it more complicated. + var newStructure = SnapshotExpression(source.PendingSelector); + var queryable = Reduce(source); + var navigationTree = new NavigationTreeExpression(newStructure); + var parameterName = source.CurrentParameter.Name ?? GetParameterName("e"); + source = new NavigationExpansionExpression(queryable, navigationTree, navigationTree, parameterName); + } + NewArrayExpression settersArray; switch (setters) { diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs index eb9fbe5ee80..43b68cf565e 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs @@ -357,12 +357,12 @@ public Task FromSqlRaw_queryable_with_parameters(bool async) AssertSql( """ -@p0='London' -@p1='Sales Representative' +@p='London' +@p0='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p AND c["ContactTitle"] = @p0 ) s """); }); @@ -388,12 +388,12 @@ public Task FromSqlRaw_queryable_with_parameters_inline(bool async) AssertSql( """ -@p0='London' -@p1='Sales Representative' +@p='London' +@p0='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p AND c["ContactTitle"] = @p0 ) s """); }); @@ -418,11 +418,11 @@ public Task FromSqlRaw_queryable_with_null_parameter(bool async) AssertSql( """ -@p0=null +@p=null SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Employee" AND c["ReportsTo"] = @p0 OR (IS_NULL(c["ReportsTo"]) AND IS_NULL(@p0)) + SELECT * FROM root c WHERE c["$type"] = "Employee" AND c["ReportsTo"] = @p OR (IS_NULL(c["ReportsTo"]) AND IS_NULL(@p)) ) s """); }); @@ -451,12 +451,12 @@ public Task FromSqlRaw_queryable_with_parameters_and_closure(bool async) AssertSql( """ -@p0='London' +@p='London' @contactTitle='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p0 + SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p ) s WHERE (s["ContactTitle"] = @contactTitle) """); @@ -540,22 +540,22 @@ public virtual Task FromSqlRaw_queryable_with_parameters_cache_key_includes_para AssertSql( """ -@p0='London' -@p1='Sales Representative' +@p='London' +@p0='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p AND c["ContactTitle"] = @p0 ) s """, // """ -@p0='Madrid' -@p1='Accounting Manager' +@p='Madrid' +@p0='Accounting Manager' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["$type"] = "Customer" AND c["City"] = @p AND c["ContactTitle"] = @p0 ) s """); }); @@ -731,12 +731,12 @@ await AssertQuery( AssertSql( """ -@p0='London' -@p1='Sales Representative' +@p='London' +@p0='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["City"] = @p AND c["ContactTitle"] = @p0 ) s """); }); @@ -754,13 +754,50 @@ await AssertQuery( AssertSql( """ -@p0='London' -@p1='Sales Representative' +@p='London' +@p0='Sales Representative' SELECT VALUE s FROM ( - SELECT * FROM root c WHERE c["City"] = @p0 AND c["ContactTitle"] = @p1 + SELECT * FROM root c WHERE c["City"] = @p AND c["ContactTitle"] = @p0 ) s +"""); + }); + + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public virtual Task Both_FromSql_and_regular_parameters(bool async) + => Fixture.NoSyncTest( + async, async a => + { + var city = "London"; + var country = "UK"; + + await AssertQuery( + a, + ss => ((DbSet)ss.Set()) + .FromSql($"""SELECT * FROM root c WHERE c["City"] = {city} AND c["Country"] = {country}""") + .OrderBy(c => c.CustomerID) + .Skip(1) // Non-lambda parameter, so becomes a SqlParameter + .Take(5), + ss => ss.Set() + .Where(x => x.City == city) + .OrderBy(c => c.CustomerID) + .Skip(1) + .Take(5)); + + AssertSql( + """ +@p='London' +@p0='UK' +@p00='1' +@p1='5' + +SELECT VALUE s +FROM ( + SELECT * FROM root c WHERE c["City"] = @p AND c["Country"] = @p0 +) s +ORDER BY s["id"] +OFFSET @p00 LIMIT @p1 """); }); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs index f91ad1dee97..d65ccd5a001 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs @@ -1555,11 +1555,11 @@ public override Task Two_parameters_with_same_case_insensitive_name_get_uniquifi AssertSql( """ @customerID='ANATR' -@customerId='ALFKI' +@customerId0='ALFKI' SELECT VALUE c FROM root c -WHERE ((c["id"] = @customerID) OR (c["id"] = @customerId)) +WHERE ((c["id"] = @customerID) OR (c["id"] = @customerId0)) """); }); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs index 749bbb60b5e..71cb9e15f11 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs @@ -791,6 +791,28 @@ WHERE ARRAY_CONTAINS(@ints, c["Int"]) """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +@ints='[]' + +SELECT VALUE c +FROM root c +WHERE ARRAY_CONTAINS(@ints, c["Int"]) +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + // Cosmos join support. Issue #16920. + await AssertTranslationFailed(base.Parameter_collection_empty_Join); + + AssertSql(); + } + public override async Task Parameter_collection_Contains_with_EF_Constant() { // #34327 diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs index dade6edb6ff..6324f7845aa 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.ModelSnapshot.cs @@ -14,6 +14,7 @@ using NetTopologySuite; using NetTopologySuite.Geometries; using Xunit.Sdk; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; // ReSharper disable InconsistentNaming // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -2022,7 +2023,7 @@ public virtual void CheckConstraint_is_only_stored_in_snapshot_once_for_TPH() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2354,7 +2355,7 @@ public virtual void BaseType_is_stored_in_snapshot() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2425,7 +2426,7 @@ public virtual void Discriminator_annotations_are_stored_in_snapshot() b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().IsComplete(true).HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").IsComplete(true).HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -2500,7 +2501,7 @@ public virtual void Converted_discriminator_annotations_are_stored_in_snapshot() b.ToTable("BaseEntityWithStructDiscriminator", "DefaultSchema"); - b.HasDiscriminator().IsComplete(true).HasValue("Base"); + b.HasDiscriminator("Discriminator").IsComplete(true).HasValue("Base"); b.UseTphMappingStrategy(); }); @@ -2527,13 +2528,18 @@ public virtual void Converted_discriminator_annotations_are_stored_in_snapshot() """), o => { + var baseEntityType = o.FindEntityType(typeof(BaseEntityWithStructDiscriminator)); Assert.Equal( "Discriminator", - o.FindEntityType(typeof(BaseEntityWithStructDiscriminator))[CoreAnnotationNames.DiscriminatorProperty]); + baseEntityType[CoreAnnotationNames.DiscriminatorProperty]); Assert.Equal( "Base", - o.FindEntityType(typeof(BaseEntityWithStructDiscriminator))[CoreAnnotationNames.DiscriminatorValue]); + baseEntityType[CoreAnnotationNames.DiscriminatorValue]); + + var discriminatorProperty = baseEntityType.FindDiscriminatorProperty(); + Assert.Equal(typeof(string), discriminatorProperty.ClrType); + Assert.Equal("Discriminator", discriminatorProperty.Name); Assert.Equal( "Another", @@ -3375,6 +3381,55 @@ public virtual void Discriminator_of_enum_to_string() Assert.False(discriminatorProperty.IsNullable); }); + [ConditionalFact] + public virtual void Discriminator_with_non_string_default_name_is_stored_in_snapshot() + => Test( + builder => + { + builder.Entity().HasBaseType(); + builder.Entity() + .Ignore(b => b.Navigation) + .HasDiscriminator("Discriminator") + .HasValue(0) + .HasValue(1); + }, + AddBoilerPlate( + GetHeading() + + """ + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Discriminator") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BaseType", "DefaultSchema"); + + b.HasDiscriminator("Discriminator").HasValue(0); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+DerivedType", b => + { + b.HasBaseType("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTest+BaseType"); + + b.HasDiscriminator().HasValue(1); + }); +"""), + model => + { + var discriminatorProperty = model.FindEntityType(typeof(BaseType))!.FindDiscriminatorProperty(); + Assert.Equal(typeof(int), discriminatorProperty.ClrType); + Assert.Equal("Discriminator", discriminatorProperty.Name); + }); + [ConditionalFact] public virtual void Temporal_table_information_is_stored_in_snapshot() => Test( @@ -4939,7 +4994,7 @@ public virtual void Property_column_name_is_stored_in_snapshot_when_DefaultColum b.ToTable("BarBase", "DefaultSchema"); - b.HasDiscriminator().HasValue("BarBase"); + b.HasDiscriminator("Discriminator").HasValue("BarBase"); b.UseTphMappingStrategy(); }); @@ -5155,7 +5210,7 @@ public virtual void Property_column_name_on_specific_table_is_stored_in_snapshot b.ToTable("BaseEntity", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseEntity"); + b.HasDiscriminator("Discriminator").HasValue("BaseEntity"); b.UseTphMappingStrategy(); }); @@ -7718,7 +7773,7 @@ public virtual void Do_not_generate_entity_type_builder_again_if_no_foreign_key_ b.ToTable("BaseType", "DefaultSchema"); - b.HasDiscriminator().HasValue("BaseType"); + b.HasDiscriminator("Discriminator").HasValue("BaseType"); b.UseTphMappingStrategy(); }); diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs index 2db7d066f6d..55b973e4059 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesRelationalTestBase.cs @@ -68,7 +68,7 @@ public override Task Update_with_invalid_lambda_in_set_property_throws(bool asyn public override Task Update_multiple_tables_throws(bool async) => AssertTranslationFailed( - RelationalStrings.MultipleTablesInExecuteUpdate("o => o.Outer.OrderDate", "o => o.Inner.ContactName"), + RelationalStrings.MultipleTablesInExecuteUpdate("o => o.e.OrderDate", "o => o.Customer.ContactName"), () => base.Update_multiple_tables_throws(async)); public override Task Update_unmapped_property_throws(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs index eefbf36c7e0..2e25c5e6038 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousRelationalTestBase.cs @@ -15,6 +15,10 @@ public ComplexJsonMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputH Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + protected void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs index 385eb0e79e0..a2f83e6fb35 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousRelationalTestBase.cs @@ -15,6 +15,10 @@ public ComplexTableSplittingMiscellaneousRelationalTestBase(TFixture fixture, IT fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + protected void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/Navigations/NavigationsMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/Navigations/NavigationsMiscellaneousRelationalTestBase.cs index 8e7fef3b2c0..2dec3c06b05 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/Navigations/NavigationsMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/Navigations/NavigationsMiscellaneousRelationalTestBase.cs @@ -13,6 +13,10 @@ public NavigationsMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputH fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + protected void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs index b63bacab57c..dde74366dca 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousRelationalTestBase.cs @@ -15,6 +15,10 @@ public OwnedJsonMiscellaneousRelationalTestBase(TFixture fixture, ITestOutputHel fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + protected void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs index 0d0961269f6..b197306b9f0 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousRelationalTestBase.cs @@ -13,6 +13,10 @@ public OwnedNavigationsMiscellaneousRelationalTestBase(TFixture fixture, ITestOu fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + public void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs index c6ef7514ca8..0acbec652b0 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousRelationalTestBase.cs @@ -15,6 +15,10 @@ public OwnedTableSplittingMiscellaneousRelationalTestBase(TFixture fixture, ITes fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper); } + [ConditionalFact] + public virtual Task FromSql_on_root() + => RelationalAssociationsTests.FromSql_on_root(this, Fixture); + protected void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Relational.Specification.Tests/Query/Associations/RelationalAssociationsTests.cs b/test/EFCore.Relational.Specification.Tests/Query/Associations/RelationalAssociationsTests.cs new file mode 100644 index 00000000000..035d091317e --- /dev/null +++ b/test/EFCore.Relational.Specification.Tests/Query/Associations/RelationalAssociationsTests.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Query.Associations; + +public class RelationalAssociationsTests +{ + internal static async Task FromSql_on_root(QueryTestBase queryTestBase, TFixture fixture) + where TFixture : SharedStoreFixtureBase, IQueryFixtureBase, new() + { + using var context = fixture.CreateContext(); + var tableName = context.Model.FindEntityType(typeof(RootEntity))?.GetTableName() + ?? throw new UnreachableException("Couldn't find relational table name for RootEntity"); + var sqlGenerationHelper = context.GetService(); + + await queryTestBase.AssertQuery( + ss => ((DbSet)ss.Set()).FromSqlRaw($"SELECT * FROM {sqlGenerationHelper.DelimitIdentifier(tableName)}"), + ss => ss.Set()); + } +} diff --git a/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs index 239ff1995b1..c91ccb1a089 100644 --- a/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs @@ -951,4 +951,26 @@ public virtual Task Update_with_two_inner_joins(bool async) s => s.SetProperty(od => od.Quantity, 1), rowsAffectedCount: 228, (b, a) => Assert.All(a, od => Assert.Equal(1, od.Quantity))); + + [ConditionalTheory, MemberData(nameof(IsAsyncData))] // #37247 + public virtual Task Update_with_PK_pushdown_and_join_and_multiple_setters(bool async) + => AssertUpdate( + async, + ss => ss + .Set() + // Not natively supported by UPDATE, so triggers PK subquery + .OrderBy(od => od.OrderID) + .Skip(1) + // Triggers JOIN with a pending selector for transparent identifier (Outer, Inner) + .Where(od => od.Order.CustomerID == "ALFKI"), + e => e, + s => s + .SetProperty(od => od.Quantity, 1) + .SetProperty(od => od.UnitPrice, 10), + rowsAffectedCount: 12, + (b, a) => Assert.All(a, od => + { + Assert.Equal(1, od.Quantity); + Assert.Equal(10, od.UnitPrice); + })); } diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index cebf04a94fe..1f916cda53d 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -468,6 +468,26 @@ await AssertQuery( assertEmpty: true); } + [ConditionalFact] + public virtual async Task Parameter_collection_empty_Contains() + { + int[] ints = []; + + await AssertQuery( + ss => ss.Set().Where(c => ints.Contains(c.Int)), + assertEmpty: true); + } + + [ConditionalFact] // #37216 + public virtual async Task Parameter_collection_empty_Join() + { + int[] ints = []; + + await AssertQuery( + ss => ss.Set().Join(ints, e => e.Id, i => i, (e, i) => e), + assertEmpty: true); + } + [ConditionalFact] public virtual Task Parameter_collection_Contains_with_EF_Constant() { diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs index e77f0187114..8152a4a77d6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs @@ -1623,6 +1623,34 @@ FROM [Order Details] AS [o] """); } + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public override async Task Update_with_PK_pushdown_and_join_and_multiple_setters(bool async) + { + await base.Update_with_PK_pushdown_and_join_and_multiple_setters(async); + + AssertExecuteUpdateSql( + """ +@p='1' +@p1='10' (DbType = Currency) + +UPDATE [o2] +SET [o2].[Quantity] = CAST(@p AS smallint), + [o2].[UnitPrice] = @p1 +FROM [Order Details] AS [o2] +INNER JOIN ( + SELECT [o1].[OrderID], [o1].[ProductID] + FROM ( + SELECT [o].[OrderID], [o].[ProductID] + FROM [Order Details] AS [o] + ORDER BY [o].[OrderID] + OFFSET @p ROWS + ) AS [o1] + INNER JOIN [Orders] AS [o0] ON [o1].[OrderID] = [o0].[OrderID] + WHERE [o0].[CustomerID] = N'ALFKI' +) AS [s] ON [o2].[OrderID] = [s].[OrderID] AND [o2].[ProductID] = [s].[ProductID] +"""); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs index e9c8ee7b3a8..341407cb441 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonMiscellaneousSqlServerTest.cs @@ -146,6 +146,19 @@ WHERE [v].[OptionalAssociate] IS NOT NULL #endregion Value types + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT [m].[Id], [m].[Name], [m].[AssociateCollection], [m].[OptionalAssociate], [m].[RequiredAssociate] +FROM ( + SELECT * FROM [RootEntity] +) AS [m] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs index 1646768532b..868095884ca 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexTableSplitting/ComplexTableSplittingMiscellaneousSqlServerTest.cs @@ -84,6 +84,16 @@ WHERE [v].[OptionalAssociate_Id] IS NOT NULL #endregion Value types + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT * FROM [RootEntity] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsMiscellaneousSqlServerTest.cs index 227db34b7c5..1b50f02e3e9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/Navigations/NavigationsMiscellaneousSqlServerTest.cs @@ -96,6 +96,35 @@ FROM [AssociateType] AS [a1] #endregion Simple filters + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT [m].[Id], [m].[Name], [m].[OptionalAssociateId], [m].[RequiredAssociateId], [a].[Id], [n].[Id], [n0].[Id], [a0].[Id], [n1].[Id], [n2].[Id], [s].[Id], [s].[CollectionRootId], [s].[Int], [s].[Ints], [s].[Name], [s].[OptionalNestedAssociateId], [s].[RequiredNestedAssociateId], [s].[String], [s].[Id0], [s].[Id1], [s].[Id2], [s].[CollectionAssociateId], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[CollectionAssociateId0], [s].[Int1], [s].[Ints1], [s].[Name1], [s].[String1], [s].[CollectionAssociateId1], [s].[Int2], [s].[Ints2], [s].[Name2], [s].[String2], [a].[CollectionRootId], [a].[Int], [a].[Ints], [a].[Name], [a].[OptionalNestedAssociateId], [a].[RequiredNestedAssociateId], [a].[String], [n6].[Id], [n6].[CollectionAssociateId], [n6].[Int], [n6].[Ints], [n6].[Name], [n6].[String], [n].[CollectionAssociateId], [n].[Int], [n].[Ints], [n].[Name], [n].[String], [n0].[CollectionAssociateId], [n0].[Int], [n0].[Ints], [n0].[Name], [n0].[String], [a0].[CollectionRootId], [a0].[Int], [a0].[Ints], [a0].[Name], [a0].[OptionalNestedAssociateId], [a0].[RequiredNestedAssociateId], [a0].[String], [n7].[Id], [n7].[CollectionAssociateId], [n7].[Int], [n7].[Ints], [n7].[Name], [n7].[String], [n1].[CollectionAssociateId], [n1].[Int], [n1].[Ints], [n1].[Name], [n1].[String], [n2].[CollectionAssociateId], [n2].[Int], [n2].[Ints], [n2].[Name], [n2].[String] +FROM ( + SELECT * FROM [RootEntity] +) AS [m] +LEFT JOIN [AssociateType] AS [a] ON [m].[OptionalAssociateId] = [a].[Id] +LEFT JOIN [NestedAssociateType] AS [n] ON [a].[OptionalNestedAssociateId] = [n].[Id] +LEFT JOIN [NestedAssociateType] AS [n0] ON [a].[RequiredNestedAssociateId] = [n0].[Id] +INNER JOIN [AssociateType] AS [a0] ON [m].[RequiredAssociateId] = [a0].[Id] +LEFT JOIN [NestedAssociateType] AS [n1] ON [a0].[OptionalNestedAssociateId] = [n1].[Id] +INNER JOIN [NestedAssociateType] AS [n2] ON [a0].[RequiredNestedAssociateId] = [n2].[Id] +LEFT JOIN ( + SELECT [a1].[Id], [a1].[CollectionRootId], [a1].[Int], [a1].[Ints], [a1].[Name], [a1].[OptionalNestedAssociateId], [a1].[RequiredNestedAssociateId], [a1].[String], [n3].[Id] AS [Id0], [n4].[Id] AS [Id1], [n5].[Id] AS [Id2], [n5].[CollectionAssociateId], [n5].[Int] AS [Int0], [n5].[Ints] AS [Ints0], [n5].[Name] AS [Name0], [n5].[String] AS [String0], [n3].[CollectionAssociateId] AS [CollectionAssociateId0], [n3].[Int] AS [Int1], [n3].[Ints] AS [Ints1], [n3].[Name] AS [Name1], [n3].[String] AS [String1], [n4].[CollectionAssociateId] AS [CollectionAssociateId1], [n4].[Int] AS [Int2], [n4].[Ints] AS [Ints2], [n4].[Name] AS [Name2], [n4].[String] AS [String2] + FROM [AssociateType] AS [a1] + LEFT JOIN [NestedAssociateType] AS [n3] ON [a1].[OptionalNestedAssociateId] = [n3].[Id] + INNER JOIN [NestedAssociateType] AS [n4] ON [a1].[RequiredNestedAssociateId] = [n4].[Id] + LEFT JOIN [NestedAssociateType] AS [n5] ON [a1].[Id] = [n5].[CollectionAssociateId] +) AS [s] ON [m].[Id] = [s].[CollectionRootId] +LEFT JOIN [NestedAssociateType] AS [n6] ON [a].[Id] = [n6].[CollectionAssociateId] +LEFT JOIN [NestedAssociateType] AS [n7] ON [a0].[Id] = [n7].[CollectionAssociateId] +ORDER BY [m].[Id], [a].[Id], [n].[Id], [n0].[Id], [a0].[Id], [n1].[Id], [n2].[Id], [s].[Id], [s].[Id0], [s].[Id1], [s].[Id2], [n6].[Id] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs index eddfb5ca660..ec204531776 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedJson/OwnedJsonMiscellaneousSqlServerTest.cs @@ -84,6 +84,19 @@ WHERE CAST(JSON_VALUE([r].[RequiredAssociate], '$.RequiredNestedAssociate.Int') #endregion Simple filters + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT [m].[Id], [m].[Name], [m].[AssociateCollection], [m].[OptionalAssociate], [m].[RequiredAssociate] +FROM ( + SELECT * FROM [RootEntity] +) AS [m] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs index 98280a9be26..e86339afe3a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedNavigations/OwnedNavigationsMiscellaneousSqlServerTest.cs @@ -96,6 +96,35 @@ FROM [RelatedCollection] AS [r3] #endregion Simple filters + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT [m].[Id], [m].[Name], [o].[RootEntityId], [o0].[AssociateTypeRootEntityId], [o1].[AssociateTypeRootEntityId], [r].[RootEntityId], [r0].[AssociateTypeRootEntityId], [r1].[AssociateTypeRootEntityId], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Ints], [s].[Name], [s].[String], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[AssociateTypeRootEntityId0], [s].[AssociateTypeId0], [s].[AssociateTypeRootEntityId1], [s].[AssociateTypeId1], [s].[Id0], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[Id1], [s].[Int1], [s].[Ints1], [s].[Name1], [s].[String1], [s].[Id2], [s].[Int2], [s].[Ints2], [s].[Name2], [s].[String2], [o].[Id], [o].[Int], [o].[Ints], [o].[Name], [o].[String], [o2].[AssociateTypeRootEntityId], [o2].[Id], [o2].[Int], [o2].[Ints], [o2].[Name], [o2].[String], [o0].[Id], [o0].[Int], [o0].[Ints], [o0].[Name], [o0].[String], [o1].[Id], [o1].[Int], [o1].[Ints], [o1].[Name], [o1].[String], [r].[Id], [r].[Int], [r].[Ints], [r].[Name], [r].[String], [r6].[AssociateTypeRootEntityId], [r6].[Id], [r6].[Int], [r6].[Ints], [r6].[Name], [r6].[String], [r0].[Id], [r0].[Int], [r0].[Ints], [r0].[Name], [r0].[String], [r1].[Id], [r1].[Int], [r1].[Ints], [r1].[Name], [r1].[String] +FROM ( + SELECT * FROM [RootEntity] +) AS [m] +LEFT JOIN [OptionalRelated] AS [o] ON [m].[Id] = [o].[RootEntityId] +LEFT JOIN [OptionalRelated_OptionalNested] AS [o0] ON [o].[RootEntityId] = [o0].[AssociateTypeRootEntityId] +LEFT JOIN [OptionalRelated_RequiredNested] AS [o1] ON [o].[RootEntityId] = [o1].[AssociateTypeRootEntityId] +LEFT JOIN [RequiredRelated] AS [r] ON [m].[Id] = [r].[RootEntityId] +LEFT JOIN [RequiredRelated_OptionalNested] AS [r0] ON [r].[RootEntityId] = [r0].[AssociateTypeRootEntityId] +LEFT JOIN [RequiredRelated_RequiredNested] AS [r1] ON [r].[RootEntityId] = [r1].[AssociateTypeRootEntityId] +LEFT JOIN ( + SELECT [r2].[RootEntityId], [r2].[Id], [r2].[Int], [r2].[Ints], [r2].[Name], [r2].[String], [r3].[AssociateTypeRootEntityId], [r3].[AssociateTypeId], [r4].[AssociateTypeRootEntityId] AS [AssociateTypeRootEntityId0], [r4].[AssociateTypeId] AS [AssociateTypeId0], [r5].[AssociateTypeRootEntityId] AS [AssociateTypeRootEntityId1], [r5].[AssociateTypeId] AS [AssociateTypeId1], [r5].[Id] AS [Id0], [r5].[Int] AS [Int0], [r5].[Ints] AS [Ints0], [r5].[Name] AS [Name0], [r5].[String] AS [String0], [r3].[Id] AS [Id1], [r3].[Int] AS [Int1], [r3].[Ints] AS [Ints1], [r3].[Name] AS [Name1], [r3].[String] AS [String1], [r4].[Id] AS [Id2], [r4].[Int] AS [Int2], [r4].[Ints] AS [Ints2], [r4].[Name] AS [Name2], [r4].[String] AS [String2] + FROM [RelatedCollection] AS [r2] + LEFT JOIN [RelatedCollection_OptionalNested] AS [r3] ON [r2].[RootEntityId] = [r3].[AssociateTypeRootEntityId] AND [r2].[Id] = [r3].[AssociateTypeId] + LEFT JOIN [RelatedCollection_RequiredNested] AS [r4] ON [r2].[RootEntityId] = [r4].[AssociateTypeRootEntityId] AND [r2].[Id] = [r4].[AssociateTypeId] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r5] ON [r2].[RootEntityId] = [r5].[AssociateTypeRootEntityId] AND [r2].[Id] = [r5].[AssociateTypeId] +) AS [s] ON [m].[Id] = [s].[RootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o2] ON [o].[RootEntityId] = [o2].[AssociateTypeRootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r6] ON [r].[RootEntityId] = [r6].[AssociateTypeRootEntityId] +ORDER BY [m].[Id], [o].[RootEntityId], [o0].[AssociateTypeRootEntityId], [o1].[AssociateTypeRootEntityId], [r].[RootEntityId], [r0].[AssociateTypeRootEntityId], [r1].[AssociateTypeRootEntityId], [s].[RootEntityId], [s].[Id], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[AssociateTypeRootEntityId0], [s].[AssociateTypeId0], [s].[AssociateTypeRootEntityId1], [s].[AssociateTypeId1], [s].[Id0], [o2].[AssociateTypeRootEntityId], [o2].[Id], [r6].[AssociateTypeRootEntityId] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs index ae95ec70fc9..fcf81893bea 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/OwnedTableSplitting/OwnedTableSplittingMiscellaneousSqlServerTest.cs @@ -78,6 +78,31 @@ WHEN [r].[OptionalAssociate_Id] IS NOT NULL AND [r].[OptionalAssociate_Int] IS N #endregion Simple filters + public override async Task FromSql_on_root() + { + await base.FromSql_on_root(); + + AssertSql( + """ +SELECT [m].[Id], [m].[Name], [r].[Id], [r0].[Id], [s].[RootEntityId], [s].[Id], [s].[Int], [s].[Ints], [s].[Name], [s].[String], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[Id0], [s].[Int0], [s].[Ints0], [s].[Name0], [s].[String0], [s].[OptionalNestedAssociate_Id], [s].[OptionalNestedAssociate_Int], [s].[OptionalNestedAssociate_Ints], [s].[OptionalNestedAssociate_Name], [s].[OptionalNestedAssociate_String], [s].[RequiredNestedAssociate_Id], [s].[RequiredNestedAssociate_Int], [s].[RequiredNestedAssociate_Ints], [s].[RequiredNestedAssociate_Name], [s].[RequiredNestedAssociate_String], [r].[OptionalAssociate_Id], [r].[OptionalAssociate_Int], [r].[OptionalAssociate_Ints], [r].[OptionalAssociate_Name], [r].[OptionalAssociate_String], [o].[AssociateTypeRootEntityId], [o].[Id], [o].[Int], [o].[Ints], [o].[Name], [o].[String], [r].[OptionalAssociate_OptionalNestedAssociate_Id], [r].[OptionalAssociate_OptionalNestedAssociate_Int], [r].[OptionalAssociate_OptionalNestedAssociate_Ints], [r].[OptionalAssociate_OptionalNestedAssociate_Name], [r].[OptionalAssociate_OptionalNestedAssociate_String], [r].[OptionalAssociate_RequiredNestedAssociate_Id], [r].[OptionalAssociate_RequiredNestedAssociate_Int], [r].[OptionalAssociate_RequiredNestedAssociate_Ints], [r].[OptionalAssociate_RequiredNestedAssociate_Name], [r].[OptionalAssociate_RequiredNestedAssociate_String], [r0].[RequiredAssociate_Id], [r0].[RequiredAssociate_Int], [r0].[RequiredAssociate_Ints], [r0].[RequiredAssociate_Name], [r0].[RequiredAssociate_String], [r3].[AssociateTypeRootEntityId], [r3].[Id], [r3].[Int], [r3].[Ints], [r3].[Name], [r3].[String], [r0].[RequiredAssociate_OptionalNestedAssociate_Id], [r0].[RequiredAssociate_OptionalNestedAssociate_Int], [r0].[RequiredAssociate_OptionalNestedAssociate_Ints], [r0].[RequiredAssociate_OptionalNestedAssociate_Name], [r0].[RequiredAssociate_OptionalNestedAssociate_String], [r0].[RequiredAssociate_RequiredNestedAssociate_Id], [r0].[RequiredAssociate_RequiredNestedAssociate_Int], [r0].[RequiredAssociate_RequiredNestedAssociate_Ints], [r0].[RequiredAssociate_RequiredNestedAssociate_Name], [r0].[RequiredAssociate_RequiredNestedAssociate_String] +FROM ( + SELECT * FROM [RootEntity] +) AS [m] +LEFT JOIN [RootEntity] AS [r] ON [m].[Id] = [r].[Id] +LEFT JOIN [RootEntity] AS [r0] ON [m].[Id] = [r0].[Id] +LEFT JOIN ( + SELECT [r1].[RootEntityId], [r1].[Id], [r1].[Int], [r1].[Ints], [r1].[Name], [r1].[String], [r2].[AssociateTypeRootEntityId], [r2].[AssociateTypeId], [r2].[Id] AS [Id0], [r2].[Int] AS [Int0], [r2].[Ints] AS [Ints0], [r2].[Name] AS [Name0], [r2].[String] AS [String0], [r1].[OptionalNestedAssociate_Id], [r1].[OptionalNestedAssociate_Int], [r1].[OptionalNestedAssociate_Ints], [r1].[OptionalNestedAssociate_Name], [r1].[OptionalNestedAssociate_String], [r1].[RequiredNestedAssociate_Id], [r1].[RequiredNestedAssociate_Int], [r1].[RequiredNestedAssociate_Ints], [r1].[RequiredNestedAssociate_Name], [r1].[RequiredNestedAssociate_String] + FROM [RelatedCollection] AS [r1] + LEFT JOIN [RelatedCollection_NestedCollection] AS [r2] ON [r1].[RootEntityId] = [r2].[AssociateTypeRootEntityId] AND [r1].[Id] = [r2].[AssociateTypeId] +) AS [s] ON [m].[Id] = [s].[RootEntityId] +LEFT JOIN [OptionalRelated_NestedCollection] AS [o] ON CASE + WHEN [r].[OptionalAssociate_Id] IS NOT NULL AND [r].[OptionalAssociate_Int] IS NOT NULL AND [r].[OptionalAssociate_Ints] IS NOT NULL AND [r].[OptionalAssociate_Name] IS NOT NULL AND [r].[OptionalAssociate_String] IS NOT NULL THEN [r].[Id] +END = [o].[AssociateTypeRootEntityId] +LEFT JOIN [RequiredRelated_NestedCollection] AS [r3] ON [r0].[Id] = [r3].[AssociateTypeRootEntityId] +ORDER BY [m].[Id], [r].[Id], [r0].[Id], [s].[RootEntityId], [s].[Id], [s].[AssociateTypeRootEntityId], [s].[AssociateTypeId], [s].[Id0], [o].[AssociateTypeRootEntityId], [o].[Id], [r3].[AssociateTypeRootEntityId] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index 920e74ae544..3e96e03ff95 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -771,6 +771,33 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + await base.Parameter_collection_empty_Join(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +INNER JOIN ( + SELECT NULL AS [Value] + WHERE 0 = 1 +) AS [p0] ON [p].[Id] = [p0].[Value] +"""); + } + public override async Task Parameter_collection_Contains_with_EF_Constant() { await base.Parameter_collection_Contains_with_EF_Constant(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs index 93ca2d9913e..cd315934d3e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs @@ -779,6 +779,33 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + await base.Parameter_collection_empty_Join(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +INNER JOIN ( + SELECT NULL AS [Value] + WHERE 0 = 1 +) AS [p0] ON [p].[Id] = [p0].[Value] +"""); + } + public override async Task Parameter_collection_Contains_with_EF_Constant() { await base.Parameter_collection_Contains_with_EF_Constant(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs index b56affe81df..90dd2b8c5ab 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs @@ -983,6 +983,33 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + await base.Parameter_collection_empty_Join(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +INNER JOIN ( + SELECT NULL AS [Value] + WHERE 0 = 1 +) AS [p0] ON [p].[Id] = [p0].[Value] +"""); + } + public override async Task Column_collection_of_ints_Contains() { await base.Column_collection_of_ints_Contains(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index 1c677e53e57..c09b7f34410 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -802,6 +802,33 @@ FROM [PrimitiveCollectionsEntity] AS [p] """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE 0 = 1 +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + await base.Parameter_collection_empty_Join(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +INNER JOIN ( + SELECT NULL AS [Value] + WHERE 0 = 1 +) AS [p0] ON [p].[Id] = [p0].[Value] +"""); + } + public override async Task Parameter_collection_Contains_with_EF_Constant() { await base.Parameter_collection_Contains_with_EF_Constant(); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs index a7118bf2a65..7de7a531f03 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs @@ -1527,6 +1527,34 @@ public override async Task Update_with_two_inner_joins(bool async) AssertExecuteUpdateSql( """ +"""); + } + + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public override async Task Update_with_PK_pushdown_and_join_and_multiple_setters(bool async) + { + await base.Update_with_PK_pushdown_and_join_and_multiple_setters(async); + + AssertExecuteUpdateSql( + """ +@p='1' +@p1='10' + +UPDATE "Order Details" AS "o2" +SET "Quantity" = CAST(@p AS INTEGER), + "UnitPrice" = @p1 +FROM ( + SELECT "o1"."OrderID", "o1"."ProductID" + FROM ( + SELECT "o"."OrderID", "o"."ProductID" + FROM "Order Details" AS "o" + ORDER BY "o"."OrderID" + LIMIT -1 OFFSET @p + ) AS "o1" + INNER JOIN "Orders" AS "o0" ON "o1"."OrderID" = "o0"."OrderID" + WHERE "o0"."CustomerID" = 'ALFKI' +) AS "s" +WHERE "o2"."OrderID" = "s"."OrderID" AND "o2"."ProductID" = "s"."ProductID" """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index ff17ae0a2de..615e1eb3070 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -790,6 +790,33 @@ WHERE 0 """); } + public override async Task Parameter_collection_empty_Contains() + { + await base.Parameter_collection_empty_Contains(); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE 0 +"""); + } + + public override async Task Parameter_collection_empty_Join() + { + await base.Parameter_collection_empty_Join(); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" +FROM "PrimitiveCollectionsEntity" AS "p" +INNER JOIN ( + SELECT NULL AS "Value" + WHERE 0 +) AS "p0" ON "p"."Id" = "p0"."Value" +"""); + } + public override async Task Parameter_collection_Contains_with_EF_Constant() { await base.Parameter_collection_Contains_with_EF_Constant();