diff --git a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs index fe1517c1c82..c443509b291 100644 --- a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs @@ -535,13 +535,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp protected override Expression VisitMember(MemberExpression memberExpression) { - if (memberExpression.Expression == null) - { - // Static members which can change value - _containsClosure - = !(memberExpression.Member is FieldInfo fieldInfo && fieldInfo.IsInitOnly); - } - + _containsClosure = memberExpression.Expression != null + || !(memberExpression.Member is FieldInfo fieldInfo && fieldInfo.IsInitOnly); return base.VisitMember(memberExpression); } diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Where.cs b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Where.cs index c7f0ea5f2c4..5c203509d71 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Where.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.Where.cs @@ -592,13 +592,17 @@ public override async Task Where_new_instance_field_access_query_cache(bool isAs await base.Where_new_instance_field_access_query_cache(isAsync); AssertSql( - @"SELECT c + @"@__InstanceFieldValue_0='London' + +SELECT c FROM root c -WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""City""] = ""London""))", +WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""City""] = @__InstanceFieldValue_0))", // - @"SELECT c + @"@__InstanceFieldValue_0='Seattle' + +SELECT c FROM root c -WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""City""] = ""Seattle""))"); +WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""City""] = @__InstanceFieldValue_0))"); } public override async Task Where_new_instance_field_access_closure_via_query_cache(bool isAsync) diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Where.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Where.cs index d5e32ef2e3d..972c9602519 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Where.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.Where.cs @@ -1260,7 +1260,7 @@ public virtual Task Where_select_many_and(bool isAsync) (cs, es) => from c in cs from e in es - // ReSharper disable ArrangeRedundantParentheses + // ReSharper disable ArrangeRedundantParentheses #pragma warning disable RCS1032 // Remove redundant parentheses. where (c.City == "London" && c.Country == "UK") && (e.City == "London" && e.Country == "UK") @@ -2059,5 +2059,44 @@ public virtual Task Where_is_conditional(bool isAsync) isAsync, ps => ps.Where(p => p is Product ? false : true)); } + + [ConditionalTheory] // issue #17172 + [MemberData(nameof(IsAsyncData))] + public virtual async Task Enclosing_class_settable_member_generates_parameter(bool isAsync) + { + SettableProperty = 4; + + await AssertQuery( + isAsync, + os => os.Where(o => o.OrderID == SettableProperty)); + + SettableProperty = 10; + + await AssertQuery( + isAsync, + os => os.Where(o => o.OrderID == SettableProperty)); + } + + [ConditionalTheory] // issue #17172 + [MemberData(nameof(IsAsyncData))] + public virtual Task Enclosing_class_readonly_member_generates_parameter(bool isAsync) + { + return AssertQuery( + isAsync, + os => os.Where(o => o.OrderID == ReadOnlyProperty)); + } + + [ConditionalTheory] // issue #17172 + [MemberData(nameof(IsAsyncData))] + public virtual Task Enclosing_class_const_member_does_not_generate_parameter(bool isAsync) + { + return AssertQuery( + isAsync, + os => os.Where(o => o.OrderID == ConstantProperty)); + } + + private int SettableProperty { get; set; } + private int ReadOnlyProperty => 5; + private const int ConstantProperty = 1; } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Where.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Where.cs index aa0fd3f572d..fdcbf69de64 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Where.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.Where.cs @@ -287,13 +287,17 @@ public override async Task Where_new_instance_field_access_query_cache(bool isAs await base.Where_new_instance_field_access_query_cache(isAsync); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + @"@__InstanceFieldValue_0='London' (Size = 4000) + +SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE ([c].[City] = N'London') AND [c].[City] IS NOT NULL", +WHERE (([c].[City] = @__InstanceFieldValue_0) AND ([c].[City] IS NOT NULL AND @__InstanceFieldValue_0 IS NOT NULL)) OR ([c].[City] IS NULL AND @__InstanceFieldValue_0 IS NULL)", // - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + @"@__InstanceFieldValue_0='Seattle' (Size = 4000) + +SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE ([c].[City] = N'Seattle') AND [c].[City] IS NOT NULL"); +WHERE (([c].[City] = @__InstanceFieldValue_0) AND ([c].[City] IS NOT NULL AND @__InstanceFieldValue_0 IS NOT NULL)) OR ([c].[City] IS NULL AND @__InstanceFieldValue_0 IS NULL)"); } public override async Task Where_new_instance_field_access_closure_via_query_cache(bool isAsync) @@ -1694,5 +1698,45 @@ WHEN CAST(1 AS bit) = CAST(1 AS bit) THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END = CAST(1 AS bit)"); } + + public override async Task Enclosing_class_settable_member_generates_parameter(bool isAsync) + { + await base.Enclosing_class_settable_member_generates_parameter(isAsync); + + AssertSql( + @"@__SettableProperty_0='4' + +SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +WHERE ([o].[OrderID] = @__SettableProperty_0) AND @__SettableProperty_0 IS NOT NULL", + // + @"@__SettableProperty_0='10' + +SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +WHERE ([o].[OrderID] = @__SettableProperty_0) AND @__SettableProperty_0 IS NOT NULL"); + } + + public override async Task Enclosing_class_readonly_member_generates_parameter(bool isAsync) + { + await base.Enclosing_class_readonly_member_generates_parameter(isAsync); + + AssertSql( + @"@__ReadOnlyProperty_0='5' + +SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +WHERE ([o].[OrderID] = @__ReadOnlyProperty_0) AND @__ReadOnlyProperty_0 IS NOT NULL"); + } + + public override async Task Enclosing_class_const_member_does_not_generate_parameter(bool isAsync) + { + await base.Enclosing_class_const_member_does_not_generate_parameter(isAsync); + + AssertSql( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Orders] AS [o] +WHERE [o].[OrderID] = 1"); + } } }