From 163007afe2ec23fe111c7dee2de62ebb31fd7bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Thu, 18 Jan 2018 19:38:57 +0100 Subject: [PATCH 1/3] Fix constant value in function cached in LINQ query * Fixes #1330 (NH-3673) * Allows a workaround for #1363 (NH-2500) --- .../Async/Linq/ConstantTest.cs | 8 +++-- src/NHibernate.Test/Linq/ConstantTest.cs | 8 +++-- .../Linq/Visitors/ExpressionKeyVisitor.cs | 29 ++++++++++--------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/ConstantTest.cs b/src/NHibernate.Test/Async/Linq/ConstantTest.cs index b5f0f05037d..4aa1b970206 100644 --- a/src/NHibernate.Test/Async/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Async/Linq/ConstantTest.cs @@ -175,11 +175,16 @@ public int GetItemValue(Product p) { return _value; } + + // Workaround for object instance caching. Without this, ObjectConstants test fails. + public override string ToString() + { + return base.ToString() + _value; + } } // Adapted from NH-2500 first test case by Andrey Titov (file NHTest3.zip) [Test] - [Ignore("Not fixed yet")] public async Task ObjectConstantsAsync() { var builder = new InfoBuilder(1); @@ -200,7 +205,6 @@ private int TestFunc(Product item, int closureValue) // Adapted from NH-3673 [Test] - [Ignore("Not fixed yet")] public async Task ConstantsInFuncCallAsync() { var closureVariable = 1; diff --git a/src/NHibernate.Test/Linq/ConstantTest.cs b/src/NHibernate.Test/Linq/ConstantTest.cs index a30118e7283..1ef27cd9f94 100644 --- a/src/NHibernate.Test/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Linq/ConstantTest.cs @@ -163,11 +163,16 @@ public int GetItemValue(Product p) { return _value; } + + // Workaround for object instance caching. Without this, ObjectConstants test fails. + public override string ToString() + { + return base.ToString() + _value; + } } // Adapted from NH-2500 first test case by Andrey Titov (file NHTest3.zip) [Test] - [Ignore("Not fixed yet")] public void ObjectConstants() { var builder = new InfoBuilder(1); @@ -188,7 +193,6 @@ private int TestFunc(Product item, int closureValue) // Adapted from NH-3673 [Test] - [Ignore("Not fixed yet")] public void ConstantsInFuncCall() { var closureVariable = 1; diff --git a/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs b/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs index 1789357cfb0..73f97d5ec55 100644 --- a/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs +++ b/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs @@ -163,20 +163,21 @@ protected override Expression VisitMethodCall(MethodCallExpression expression) { var old = insideSelectClause; - switch (expression.Method.Name) - { - case "First": - case "FirstOrDefault": - case "Single": - case "SingleOrDefault": - case "Select": - case "GroupBy": - insideSelectClause = true; - break; - default: - insideSelectClause = false; - break; - } + if (expression.Method.DeclaringType?.Namespace == "System.Linq") + switch (expression.Method.Name) + { + case "First": + case "FirstOrDefault": + case "Single": + case "SingleOrDefault": + case "Select": + case "GroupBy": + insideSelectClause = true; + break; + default: + insideSelectClause = false; + break; + } Visit(expression.Object); _string.Append('.'); From ee6e31afad7e19c2a0181f2eecedae5d71780070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= Date: Fri, 19 Jan 2018 11:56:28 +0100 Subject: [PATCH 2/3] Add a test for generated keys with constants only in where. --- .../Async/Linq/ConstantTest.cs | 1 + src/NHibernate.Test/Linq/ConstantTest.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/NHibernate.Test/Async/Linq/ConstantTest.cs b/src/NHibernate.Test/Async/Linq/ConstantTest.cs index 4aa1b970206..1c8af3d4a20 100644 --- a/src/NHibernate.Test/Async/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Async/Linq/ConstantTest.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using NHibernate.DomainModel.Northwind.Entities; +using NHibernate.Linq.Visitors; using NUnit.Framework; using NHibernate.Linq; diff --git a/src/NHibernate.Test/Linq/ConstantTest.cs b/src/NHibernate.Test/Linq/ConstantTest.cs index 1ef27cd9f94..854ad12c7c0 100644 --- a/src/NHibernate.Test/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Linq/ConstantTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NHibernate.DomainModel.Northwind.Entities; +using NHibernate.Linq.Visitors; using NUnit.Framework; namespace NHibernate.Test.Linq @@ -205,5 +206,24 @@ public void ConstantsInFuncCall() Assert.That(v1, Is.EqualTo(1), "v1"); Assert.That(v2, Is.EqualTo(2), "v2"); } + + [Test] + public void ConstantInWhereDoesNotCauseManyKeys() + { + var q1 = (from c in db.Customers + where c.CustomerId == "ALFKI" + select c); + var q2 = (from c in db.Customers + where c.CustomerId == "ANATR" + select c); + var parameters1 = ExpressionParameterVisitor.Visit(q1.Expression, Sfi); + var k1 = ExpressionKeyVisitor.Visit(q1.Expression, parameters1); + var parameters2 = ExpressionParameterVisitor.Visit(q2.Expression, Sfi); + var k2 = ExpressionKeyVisitor.Visit(q2.Expression, parameters2); + + Assert.That(parameters1, Has.Count.GreaterThan(0), "parameters1"); + Assert.That(parameters2, Has.Count.GreaterThan(0), "parameters2"); + Assert.That(k2, Is.EqualTo(k1)); + } } } From 3526ada90561005f302fde4d3f2130de3e129e21 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Sat, 20 Jan 2018 22:42:45 +1300 Subject: [PATCH 3/3] Remove ToString workaround --- .../Async/Linq/ConstantTest.cs | 6 ----- src/NHibernate.Test/Linq/ConstantTest.cs | 6 ----- .../Linq/Visitors/ExpressionKeyVisitor.cs | 24 +++++++++++-------- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/ConstantTest.cs b/src/NHibernate.Test/Async/Linq/ConstantTest.cs index 1c8af3d4a20..35e8c9bfc3d 100644 --- a/src/NHibernate.Test/Async/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Async/Linq/ConstantTest.cs @@ -176,12 +176,6 @@ public int GetItemValue(Product p) { return _value; } - - // Workaround for object instance caching. Without this, ObjectConstants test fails. - public override string ToString() - { - return base.ToString() + _value; - } } // Adapted from NH-2500 first test case by Andrey Titov (file NHTest3.zip) diff --git a/src/NHibernate.Test/Linq/ConstantTest.cs b/src/NHibernate.Test/Linq/ConstantTest.cs index 854ad12c7c0..24d474951e0 100644 --- a/src/NHibernate.Test/Linq/ConstantTest.cs +++ b/src/NHibernate.Test/Linq/ConstantTest.cs @@ -164,12 +164,6 @@ public int GetItemValue(Product p) { return _value; } - - // Workaround for object instance caching. Without this, ObjectConstants test fails. - public override string ToString() - { - return base.ToString() + _value; - } } // Adapted from NH-2500 first test case by Andrey Titov (file NHTest3.zip) diff --git a/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs b/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs index 73f97d5ec55..ee053687006 100644 --- a/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs +++ b/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs @@ -109,19 +109,23 @@ protected override Expression VisitConstant(ConstantExpression expression) { _string.Append("NULL"); } - else + else if (expression.Value is IQueryable) { - var value = expression.Value as IEnumerable; - if (value != null && !(value is string) && !(value is IQueryable)) - { - _string.Append("{"); - _string.Append(String.Join(",", value.Cast())); - _string.Append("}"); - } - else + _string.Append(expression.Value); + } + else if (expression.Value is IEnumerable enumerable && !(enumerable is string)) + { + _string.Append("{"); + foreach (var value in enumerable) { - _string.Append(expression.Value); + _string.Append(value).Append("#").Append(value.GetHashCode()).Append(","); } + + _string.Append("}"); + } + else + { + _string.Append(expression.Value).Append("#").Append(expression.Value.GetHashCode()); } }