From e08d046e7f21880137d7e4c43c1f06cee3f5c83e Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Wed, 22 Feb 2023 13:38:13 +0100 Subject: [PATCH 01/15] Add Enum Equals support --- src/NHibernate.Test/Linq/FunctionTests.cs | 10 +++++ .../DefaultLinqToHqlGeneratorsRegistry.cs | 1 + .../Linq/Functions/EnumEqualsGenerator.cs | 38 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs diff --git a/src/NHibernate.Test/Linq/FunctionTests.cs b/src/NHibernate.Test/Linq/FunctionTests.cs index b92e7b3ece9..71bc64fb7c8 100644 --- a/src/NHibernate.Test/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Linq/FunctionTests.cs @@ -491,6 +491,16 @@ where item.Discount.Equals(-1) ObjectDumper.Write(query); } + [Test] + public void WhereEnumEqual() + { + var query = from item in db.PatientRecords + where item.Gender.Equals(Gender.Female) + select item; + + ObjectDumper.Write(query); + } + [Test] public void WhereEquatableEqual() { diff --git a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs index 29595877d9f..135e8f502ce 100644 --- a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs +++ b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs @@ -39,6 +39,7 @@ public DefaultLinqToHqlGeneratorsRegistry() this.Merge(new EndsWithGenerator()); this.Merge(new ContainsGenerator()); this.Merge(new EqualsGenerator()); + this.Merge(new EnumEqualsGenerator()); this.Merge(new ToUpperGenerator()); this.Merge(new ToLowerGenerator()); this.Merge(new SubStringGenerator()); diff --git a/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs b/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs new file mode 100644 index 00000000000..69de829bdb2 --- /dev/null +++ b/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Reflection; +using NHibernate.Hql.Ast; +using NHibernate.Linq.Visitors; +using NHibernate.Util; + +namespace NHibernate.Linq.Functions +{ + /// + /// Not using class as there is very special handling of enums in expressions and equality operator + /// + public class EnumEqualsGenerator : BaseHqlGeneratorForMethod + { + internal static HashSet Methods = new HashSet + { + ReflectHelper.GetMethodDefinition(x => x.Equals(default(object))), + ReflectHelper.GetMethodDefinition>(x => x.Equals(default(Enum))) + }; + + public EnumEqualsGenerator() + { + SupportedMethods = Methods; + } + + public override bool AllowsNullableReturnType(MethodInfo method) => false; + + public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) + { + Expression lhs = arguments.Count == 1 ? targetObject : arguments[0]; + Expression rhs = arguments.Count == 1 ? arguments[0] : arguments[1]; + + return treeBuilder.Equality(visitor.Visit(lhs).AsExpression(), visitor.Visit(rhs).AsExpression()); + } + } +} From 891600da4a5e7bafd0d0bc9d108c29a65aa15d9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Feb 2023 12:47:24 +0000 Subject: [PATCH 02/15] Generate async files --- src/NHibernate.Test/Async/Linq/FunctionTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/NHibernate.Test/Async/Linq/FunctionTests.cs b/src/NHibernate.Test/Async/Linq/FunctionTests.cs index e47e3d22c37..9065e77ecb3 100644 --- a/src/NHibernate.Test/Async/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Async/Linq/FunctionTests.cs @@ -459,6 +459,16 @@ where item.Discount.Equals(-1) await (ObjectDumper.WriteAsync(query)); } + [Test] + public async Task WhereEnumEqualAsync() + { + var query = from item in db.PatientRecords + where item.Gender.Equals(Gender.Female) + select item; + + await (ObjectDumper.WriteAsync(query)); + } + [Test] public async Task WhereEquatableEqualAsync() { From 60a708e9ed978dac23c2179d9e4b3ff2e031edfc Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Thu, 23 Feb 2023 10:48:37 +0100 Subject: [PATCH 03/15] move enum equals implementation to IExpressionTransformer because special expression expression behavior --- src/NHibernate.Test/Linq/FunctionTests.cs | 98 ++++++++++--------- .../EnumEqualsTransformer.cs | 50 ++++++++++ .../DefaultLinqToHqlGeneratorsRegistry.cs | 1 - .../Linq/Functions/EnumEqualsGenerator.cs | 38 ------- src/NHibernate/Linq/NhRelinqQueryParser.cs | 7 +- 5 files changed, 105 insertions(+), 89 deletions(-) create mode 100644 src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs delete mode 100644 src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs diff --git a/src/NHibernate.Test/Linq/FunctionTests.cs b/src/NHibernate.Test/Linq/FunctionTests.cs index 71bc64fb7c8..51f70bd1f3d 100644 --- a/src/NHibernate.Test/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Linq/FunctionTests.cs @@ -36,8 +36,8 @@ public void LikeFunctionWithEscapeCharacter() session.Flush(); var query = (from e in db.Employees - where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) - select e).ToList(); + where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo(employeeName)); @@ -81,8 +81,8 @@ where NHibernate.Test.Linq.FunctionTests.SqlMethods.Like(e.FirstName, "Ma%et") public void SubstringFunction2() { var query = (from e in db.Employees - where e.FirstName.Substring(0, 2) == "An" - select e).ToList(); + where e.FirstName.Substring(0, 2) == "An" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(2)); } @@ -91,8 +91,8 @@ where e.FirstName.Substring(0, 2) == "An" public void SubstringFunction1() { var query = (from e in db.Employees - where e.FirstName.Substring(3) == "rew" - select e).ToList(); + where e.FirstName.Substring(3) == "rew" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo("Andrew")); @@ -130,21 +130,21 @@ public void ReplaceFunction() var query = from e in db.Employees where e.FirstName.StartsWith("An") select new - { - Before = e.FirstName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethod = e.FirstName.Replace("An", "Zan"), - AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), - AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), + { + Before = e.FirstName, + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethod = e.FirstName.Replace("An", "Zan"), + AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), + AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), BeforeConst = suppliedName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethodConst = suppliedName.Replace("An", "Zan"), - AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), - AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethodConst = suppliedName.Replace("An", "Zan"), + AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), + AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") }; var results = query.ToList(); var s = ObjectDumper.Write(results); @@ -171,12 +171,12 @@ where e.FirstName.StartsWith("An") // Should cause ReplaceWithEvaluation to fail suppliedName = null; var failingQuery = from e in db.Employees - where e.FirstName.StartsWith("An") - select new - { - Before = e.FirstName, - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") - }; + where e.FirstName.StartsWith("An") + select new + { + Before = e.FirstName, + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") + }; Assert.That(() => failingQuery.ToList(), Throws.InstanceOf().And.InnerException.InstanceOf()); } @@ -248,7 +248,7 @@ where lowerName.Contains("a") public void TwoFunctionExpression() { var query = from e in db.Employees - where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month + where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month select e.FirstName; ObjectDumper.Write(query); @@ -285,9 +285,9 @@ public void Trim() { using (session.BeginTransaction()) { - AnotherEntity ae1 = new AnotherEntity {Input = " hi "}; - AnotherEntity ae2 = new AnotherEntity {Input = "hi"}; - AnotherEntity ae3 = new AnotherEntity {Input = "heh"}; + AnotherEntity ae1 = new AnotherEntity { Input = " hi " }; + AnotherEntity ae2 = new AnotherEntity { Input = "hi" }; + AnotherEntity ae3 = new AnotherEntity { Input = "heh" }; session.Save(ae1); session.Save(ae2); session.Save(ae3); @@ -303,7 +303,7 @@ public void Trim() // Check when passed as array // (the single character parameter is a new overload in .netcoreapp2.0, but not net461 or .netstandard2.0). - Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new [] { 'h' }) == "e")); + Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new[] { 'h' }) == "e")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimStart(new[] { 'h' }) == "eh")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimEnd(new[] { 'h' }) == "he")); @@ -316,9 +316,9 @@ public void TrimInitialWhitespace() { using (session.BeginTransaction()) { - session.Save(new AnotherEntity {Input = " hi"}); - session.Save(new AnotherEntity {Input = "hi"}); - session.Save(new AnotherEntity {Input = "heh"}); + session.Save(new AnotherEntity { Input = " hi" }); + session.Save(new AnotherEntity { Input = "hi" }); + session.Save(new AnotherEntity { Input = "heh" }); session.Flush(); Assert.That(session.Query().Count(e => e.Input.TrimStart() == "hi"), Is.EqualTo(2)); @@ -372,7 +372,7 @@ public void WhereBoolConstantEqual() var query = from item in db.Role where item.IsActive.Equals(true) select item; - + ObjectDumper.Write(query); } @@ -382,7 +382,7 @@ public void WhereBoolConditionEquals() var query = from item in db.Role where item.IsActive.Equals(item.Name != null) select item; - + ObjectDumper.Write(query); } @@ -392,7 +392,7 @@ public void WhereBoolParameterEqual() var query = from item in db.Role where item.IsActive.Equals(1 == 1) select item; - + ObjectDumper.Write(query); } @@ -412,8 +412,8 @@ where item.IsActive.Equals(f()) public void WhereLongEqual() { var query = from item in db.PatientRecords - where item.Id.Equals(-1) - select item; + where item.Id.Equals(-1) + select item; ObjectDumper.Write(query); } @@ -427,7 +427,7 @@ where item.RegisteredAt.Equals(DateTime.Today) ObjectDumper.Write(query); } - + [Test] public void WhereGuidEqual() { @@ -436,7 +436,7 @@ where item.Reference.Equals(Guid.Empty) select item; ObjectDumper.Write(query); - } + } [Test] public void WhereDoubleEqual() @@ -446,8 +446,8 @@ where item.BodyWeight.Equals(-1) select item; ObjectDumper.Write(query); - } - + } + [Test] [Ignore("Not mapped entity")] public void WhereFloatEqual() @@ -457,7 +457,7 @@ where item.Float.Equals(-1) select item; ObjectDumper.Write(query); - } + } [Test] [Ignore("Not mapped entity")] @@ -499,14 +499,20 @@ where item.Gender.Equals(Gender.Female) select item; ObjectDumper.Write(query); + + query = from item in db.PatientRecords + where item.Gender.Equals(item.Gender) + select item; + + ObjectDumper.Write(query); } [Test] public void WhereEquatableEqual() { var query = from item in db.Shippers - where ((IEquatable) item.Reference).Equals(Guid.Empty) - select item; + where ((IEquatable) item.Reference).Equals(Guid.Empty) + select item; ObjectDumper.Write(query); } diff --git a/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs b/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs new file mode 100644 index 00000000000..de005aea57a --- /dev/null +++ b/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq.Expressions; +using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; + +namespace NHibernate.Linq.ExpressionTransformers +{ + /// + /// Transforms .Equals method to equality operator. + /// This cannot be done easily in as Equals operator + /// is boxed to ((object)Enum).Equals((object)EnumValue) expression. + /// + public class EnumEqualsTransformer : IExpressionTransformer + { + public ExpressionType[] SupportedExpressionTypes => _supportedExpressionTypes; + + private static readonly ExpressionType[] _supportedExpressionTypes = new[] + { + ExpressionType.Call + }; + + public Expression Transform(MethodCallExpression expression) + { + if (expression.Object?.Type.IsEnum == true && + expression.Method.Name == nameof(Enum.Equals) && + expression.Arguments.Count == 1) + { + return Expression.Equal(expression.Object, Unwrap(expression.Arguments[0], expression.Object.Type)); + } + + return expression; + } + + private Expression Unwrap(Expression expression, System.Type type) + { + // 1) unwrap convert operand as convert is converting from enum type to object + if (expression is UnaryExpression u && u.NodeType == ExpressionType.Convert) + { + return u.Operand; + } + + // 2) convert constant expression which is of type object + if (expression is ConstantExpression c) + { + return Expression.Convert(c, type); + } + + return expression; + } + } +} diff --git a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs index 135e8f502ce..29595877d9f 100644 --- a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs +++ b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs @@ -39,7 +39,6 @@ public DefaultLinqToHqlGeneratorsRegistry() this.Merge(new EndsWithGenerator()); this.Merge(new ContainsGenerator()); this.Merge(new EqualsGenerator()); - this.Merge(new EnumEqualsGenerator()); this.Merge(new ToUpperGenerator()); this.Merge(new ToLowerGenerator()); this.Merge(new SubStringGenerator()); diff --git a/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs b/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs deleted file mode 100644 index 69de829bdb2..00000000000 --- a/src/NHibernate/Linq/Functions/EnumEqualsGenerator.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq.Expressions; -using System.Reflection; -using NHibernate.Hql.Ast; -using NHibernate.Linq.Visitors; -using NHibernate.Util; - -namespace NHibernate.Linq.Functions -{ - /// - /// Not using class as there is very special handling of enums in expressions and equality operator - /// - public class EnumEqualsGenerator : BaseHqlGeneratorForMethod - { - internal static HashSet Methods = new HashSet - { - ReflectHelper.GetMethodDefinition(x => x.Equals(default(object))), - ReflectHelper.GetMethodDefinition>(x => x.Equals(default(Enum))) - }; - - public EnumEqualsGenerator() - { - SupportedMethods = Methods; - } - - public override bool AllowsNullableReturnType(MethodInfo method) => false; - - public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) - { - Expression lhs = arguments.Count == 1 ? targetObject : arguments[0]; - Expression rhs = arguments.Count == 1 ? arguments[0] : arguments[1]; - - return treeBuilder.Equality(visitor.Visit(lhs).AsExpression(), visitor.Visit(rhs).AsExpression()); - } - } -} diff --git a/src/NHibernate/Linq/NhRelinqQueryParser.cs b/src/NHibernate/Linq/NhRelinqQueryParser.cs index eba4a3d6283..5e280ad35e9 100644 --- a/src/NHibernate/Linq/NhRelinqQueryParser.cs +++ b/src/NHibernate/Linq/NhRelinqQueryParser.cs @@ -4,10 +4,8 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using NHibernate.Engine; using NHibernate.Linq.ExpressionTransformers; using NHibernate.Linq.Visitors; -using NHibernate.Param; using NHibernate.Util; using Remotion.Linq; using Remotion.Linq.EagerFetching.Parsing; @@ -27,6 +25,7 @@ static NhRelinqQueryParser() var transformerRegistry = ExpressionTransformerRegistry.CreateDefault(); transformerRegistry.Register(new RemoveRedundantCast()); transformerRegistry.Register(new SimplifyCompareTransformer()); + transformerRegistry.Register(new EnumEqualsTransformer()); // If needing a compound processor for adding other processing, do not use // ExpressionTreeParser.CreateDefaultProcessor(transformerRegistry), it would @@ -114,14 +113,14 @@ public NHibernateNodeTypeProvider() new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetch, default(INhFetchRequest), default(Expression>)) }, typeof(ThenFetchOneExpressionNode)); methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition( EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, typeof(ThenFetchManyExpressionNode)); methodInfoRegistry.Register( new[] { ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IQueryable), default(LockMode)), ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IEnumerable), default(LockMode)) - }, + }, typeof(LockExpressionNode)); var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); From e52e7ee98bc5db0a788ab8e39efe75aee1d348be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Feb 2023 09:51:58 +0000 Subject: [PATCH 04/15] Generate async files --- .../Async/Linq/FunctionTests.cs | 94 ++++++++++--------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/FunctionTests.cs b/src/NHibernate.Test/Async/Linq/FunctionTests.cs index 9065e77ecb3..2c7906dfb85 100644 --- a/src/NHibernate.Test/Async/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Async/Linq/FunctionTests.cs @@ -48,8 +48,8 @@ public async Task LikeFunctionWithEscapeCharacterAsync() await (session.FlushAsync()); var query = await ((from e in db.Employees - where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) - select e).ToListAsync()); + where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo(employeeName)); @@ -93,8 +93,8 @@ where NHibernate.Test.Linq.FunctionTestsAsync.SqlMethods.Like(e.FirstName, "Ma%e public async Task SubstringFunction2Async() { var query = await ((from e in db.Employees - where e.FirstName.Substring(0, 2) == "An" - select e).ToListAsync()); + where e.FirstName.Substring(0, 2) == "An" + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(2)); } @@ -103,8 +103,8 @@ where e.FirstName.Substring(0, 2) == "An" public async Task SubstringFunction1Async() { var query = await ((from e in db.Employees - where e.FirstName.Substring(3) == "rew" - select e).ToListAsync()); + where e.FirstName.Substring(3) == "rew" + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo("Andrew")); @@ -142,21 +142,21 @@ public async Task ReplaceFunctionAsync() var query = from e in db.Employees where e.FirstName.StartsWith("An") select new - { - Before = e.FirstName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethod = e.FirstName.Replace("An", "Zan"), - AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), - AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), + { + Before = e.FirstName, + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethod = e.FirstName.Replace("An", "Zan"), + AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), + AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), BeforeConst = suppliedName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethodConst = suppliedName.Replace("An", "Zan"), - AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), - AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethodConst = suppliedName.Replace("An", "Zan"), + AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), + AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") }; var results = await (query.ToListAsync()); var s = await (ObjectDumper.WriteAsync(results)); @@ -183,12 +183,12 @@ where e.FirstName.StartsWith("An") // Should cause ReplaceWithEvaluation to fail suppliedName = null; var failingQuery = from e in db.Employees - where e.FirstName.StartsWith("An") - select new - { - Before = e.FirstName, - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") - }; + where e.FirstName.StartsWith("An") + select new + { + Before = e.FirstName, + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") + }; Assert.That(() => failingQuery.ToListAsync(), Throws.InstanceOf().And.InnerException.InstanceOf()); } @@ -260,7 +260,7 @@ where lowerName.Contains("a") public async Task TwoFunctionExpressionAsync() { var query = from e in db.Employees - where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month + where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month select e.FirstName; await (ObjectDumper.WriteAsync(query)); @@ -297,9 +297,9 @@ public async Task TrimAsync() { using (session.BeginTransaction()) { - AnotherEntity ae1 = new AnotherEntity {Input = " hi "}; - AnotherEntity ae2 = new AnotherEntity {Input = "hi"}; - AnotherEntity ae3 = new AnotherEntity {Input = "heh"}; + AnotherEntity ae1 = new AnotherEntity { Input = " hi " }; + AnotherEntity ae2 = new AnotherEntity { Input = "hi" }; + AnotherEntity ae3 = new AnotherEntity { Input = "heh" }; await (session.SaveAsync(ae1)); await (session.SaveAsync(ae2)); await (session.SaveAsync(ae3)); @@ -315,7 +315,7 @@ public async Task TrimAsync() // Check when passed as array // (the single character parameter is a new overload in .netcoreapp2.0, but not net461 or .netstandard2.0). - Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.Trim(new [] { 'h' }) == "e"))); + Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.Trim(new[] { 'h' }) == "e"))); Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.TrimStart(new[] { 'h' }) == "eh"))); Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.TrimEnd(new[] { 'h' }) == "he"))); @@ -328,9 +328,9 @@ public async Task TrimInitialWhitespaceAsync() { using (session.BeginTransaction()) { - await (session.SaveAsync(new AnotherEntity {Input = " hi"})); - await (session.SaveAsync(new AnotherEntity {Input = "hi"})); - await (session.SaveAsync(new AnotherEntity {Input = "heh"})); + await (session.SaveAsync(new AnotherEntity { Input = " hi" })); + await (session.SaveAsync(new AnotherEntity { Input = "hi" })); + await (session.SaveAsync(new AnotherEntity { Input = "heh" })); await (session.FlushAsync()); Assert.That(await (session.Query().CountAsync(e => e.Input.TrimStart() == "hi")), Is.EqualTo(2)); @@ -373,7 +373,7 @@ public async Task WhereBoolConstantEqualAsync() var query = from item in db.Role where item.IsActive.Equals(true) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -383,7 +383,7 @@ public async Task WhereBoolConditionEqualsAsync() var query = from item in db.Role where item.IsActive.Equals(item.Name != null) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -393,7 +393,7 @@ public async Task WhereBoolParameterEqualAsync() var query = from item in db.Role where item.IsActive.Equals(1 == 1) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -413,8 +413,8 @@ where item.IsActive.Equals(f()) public async Task WhereLongEqualAsync() { var query = from item in db.PatientRecords - where item.Id.Equals(-1) - select item; + where item.Id.Equals(-1) + select item; await (ObjectDumper.WriteAsync(query)); } @@ -428,7 +428,7 @@ where item.RegisteredAt.Equals(DateTime.Today) await (ObjectDumper.WriteAsync(query)); } - + [Test] public async Task WhereGuidEqualAsync() { @@ -437,7 +437,7 @@ where item.Reference.Equals(Guid.Empty) select item; await (ObjectDumper.WriteAsync(query)); - } + } [Test] public async Task WhereDoubleEqualAsync() @@ -447,7 +447,7 @@ where item.BodyWeight.Equals(-1) select item; await (ObjectDumper.WriteAsync(query)); - } + } [Test] public async Task WhereDecimalEqualAsync() @@ -467,14 +467,20 @@ where item.Gender.Equals(Gender.Female) select item; await (ObjectDumper.WriteAsync(query)); + + query = from item in db.PatientRecords + where item.Gender.Equals(item.Gender) + select item; + + await (ObjectDumper.WriteAsync(query)); } [Test] public async Task WhereEquatableEqualAsync() { var query = from item in db.Shippers - where ((IEquatable) item.Reference).Equals(Guid.Empty) - select item; + where ((IEquatable) item.Reference).Equals(Guid.Empty) + select item; await (ObjectDumper.WriteAsync(query)); } From 5efbcc4d38e35b081efe8035ec4963d451058cde Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Thu, 23 Feb 2023 11:18:44 +0100 Subject: [PATCH 05/15] fix for DeepSource analysis --- .../EnumEqualsTransformer.cs | 5 +- .../Linq/NHibernateNodeTypeProvider.cs | 64 +++++++++++++++++++ src/NHibernate/Linq/NhRelinqQueryParser.cs | 57 ----------------- 3 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 src/NHibernate/Linq/NHibernateNodeTypeProvider.cs diff --git a/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs b/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs index de005aea57a..92bf6b3ef87 100644 --- a/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs +++ b/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs @@ -13,10 +13,7 @@ public class EnumEqualsTransformer : IExpressionTransformer _supportedExpressionTypes; - private static readonly ExpressionType[] _supportedExpressionTypes = new[] - { - ExpressionType.Call - }; + private static readonly ExpressionType[] _supportedExpressionTypes = { ExpressionType.Call }; public Expression Transform(MethodCallExpression expression) { diff --git a/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs b/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs new file mode 100644 index 00000000000..c943894ee47 --- /dev/null +++ b/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using NHibernate.Util; +using Remotion.Linq.EagerFetching.Parsing; +using Remotion.Linq.Parsing.Structure; +using Remotion.Linq.Parsing.Structure.NodeTypeProviders; + +namespace NHibernate.Linq +{ + public class NHibernateNodeTypeProvider : INodeTypeProvider + { + private INodeTypeProvider defaultNodeTypeProvider; + + public NHibernateNodeTypeProvider() + { + var methodInfoRegistry = new MethodInfoBasedNodeTypeRegistry(); + + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.Fetch, default(IQueryable), default(Expression>)) }, + typeof(FetchOneExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchLazyProperties, default(IQueryable)) }, + typeof(FetchLazyPropertiesExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchMany, default(IQueryable), default(Expression>>)) }, + typeof(FetchManyExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetch, default(INhFetchRequest), default(Expression>)) }, + typeof(ThenFetchOneExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, + typeof(ThenFetchManyExpressionNode)); + methodInfoRegistry.Register( + new[] + { + ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IQueryable), default(LockMode)), + ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IEnumerable), default(LockMode)) + }, + typeof(LockExpressionNode)); + + var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); + nodeTypeProvider.InnerProviders.Add(methodInfoRegistry); + defaultNodeTypeProvider = nodeTypeProvider; + } + + public bool IsRegistered(MethodInfo method) + { + // Avoid Relinq turning IDictionary.Contains into ContainsResultOperator. We do our own processing for that method. + if (method.DeclaringType == typeof(IDictionary) && method.Name == "Contains") + return false; + + return defaultNodeTypeProvider.IsRegistered(method); + } + + public System.Type GetNodeType(MethodInfo method) + { + return defaultNodeTypeProvider.GetNodeType(method); + } + } +} diff --git a/src/NHibernate/Linq/NhRelinqQueryParser.cs b/src/NHibernate/Linq/NhRelinqQueryParser.cs index 5e280ad35e9..a2c7e2f6c86 100644 --- a/src/NHibernate/Linq/NhRelinqQueryParser.cs +++ b/src/NHibernate/Linq/NhRelinqQueryParser.cs @@ -1,18 +1,12 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; using NHibernate.Linq.ExpressionTransformers; using NHibernate.Linq.Visitors; -using NHibernate.Util; using Remotion.Linq; -using Remotion.Linq.EagerFetching.Parsing; using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; using Remotion.Linq.Parsing.Structure; using Remotion.Linq.Parsing.Structure.ExpressionTreeProcessors; -using Remotion.Linq.Parsing.Structure.NodeTypeProviders; namespace NHibernate.Linq { @@ -91,55 +85,4 @@ internal static Func CreatePreTransformer(IExpressionTra return new TransformingExpressionTreeProcessor(preTransformerRegistry).Process; } } - - public class NHibernateNodeTypeProvider : INodeTypeProvider - { - private INodeTypeProvider defaultNodeTypeProvider; - - public NHibernateNodeTypeProvider() - { - var methodInfoRegistry = new MethodInfoBasedNodeTypeRegistry(); - - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.Fetch, default(IQueryable), default(Expression>)) }, - typeof(FetchOneExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchLazyProperties, default(IQueryable)) }, - typeof(FetchLazyPropertiesExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchMany, default(IQueryable), default(Expression>>)) }, - typeof(FetchManyExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetch, default(INhFetchRequest), default(Expression>)) }, - typeof(ThenFetchOneExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, - typeof(ThenFetchManyExpressionNode)); - methodInfoRegistry.Register( - new[] - { - ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IQueryable), default(LockMode)), - ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IEnumerable), default(LockMode)) - }, - typeof(LockExpressionNode)); - - var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); - nodeTypeProvider.InnerProviders.Add(methodInfoRegistry); - defaultNodeTypeProvider = nodeTypeProvider; - } - - public bool IsRegistered(MethodInfo method) - { - // Avoid Relinq turning IDictionary.Contains into ContainsResultOperator. We do our own processing for that method. - if (method.DeclaringType == typeof(IDictionary) && method.Name == "Contains") - return false; - - return defaultNodeTypeProvider.IsRegistered(method); - } - - public System.Type GetNodeType(MethodInfo method) - { - return defaultNodeTypeProvider.GetNodeType(method); - } - } } From a696b45ff3740b13574d8f499b088bb5ffdd2050 Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Mon, 27 Mar 2023 09:23:26 +0200 Subject: [PATCH 06/15] revert implementation to enum equals generator add object equals implementation --- .../Async/Linq/CustomExtensionsExample.cs | 10 +-- .../Linq/CustomExtensionsExample.cs | 35 ++-------- src/NHibernate.Test/Linq/FunctionTests.cs | 11 ++++ .../EnumEqualsTransformer.cs | 47 -------------- .../Linq/Functions/EqualsGenerator.cs | 10 ++- .../Linq/NHibernateNodeTypeProvider.cs | 64 ------------------- src/NHibernate/Linq/NhRelinqQueryParser.cs | 60 ++++++++++++++++- 7 files changed, 84 insertions(+), 153 deletions(-) delete mode 100644 src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs delete mode 100644 src/NHibernate/Linq/NHibernateNodeTypeProvider.cs diff --git a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs index e1d32918feb..01141b2f240 100644 --- a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs @@ -32,15 +32,7 @@ public class CustomExtensionsExampleAsync : LinqTestCase protected override void Configure(NHibernate.Cfg.Configuration configuration) { configuration.LinqToHqlGeneratorsRegistry(); - } - - [Test] - public async Task CanUseObjectEqualsAsync() - { - var users = await (db.Users.Where(o => ((object) EnumStoredAsString.Medium).Equals(o.NullableEnum1)).ToListAsync()); - Assert.That(users.Count, Is.EqualTo(2)); - Assert.That(users.All(c => c.NullableEnum1 == EnumStoredAsString.Medium), Is.True); - } + } [Test(Description = "GH-2963")] public async Task CanUseComparisonWithExtensionOnMappedPropertyAsync() diff --git a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs index 58e6a5e0f4d..919bf6b5183 100644 --- a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs @@ -4,7 +4,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; -using NHibernate.DomainModel.Northwind.Entities; using NHibernate.Hql.Ast; using NHibernate.Linq.Functions; using NHibernate.Linq.Visitors; @@ -19,7 +18,7 @@ public static bool IsLike(this string source, string pattern) { pattern = Regex.Escape(pattern); pattern = pattern.Replace("%", ".*?").Replace("_", "."); - pattern = pattern.Replace(@"\[", "[").Replace(@"\]","]").Replace(@"\^", "^"); + pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^"); return Regex.IsMatch(source, pattern); } @@ -30,13 +29,12 @@ public static TimeSpan GetTime(this DateTime dateTime) } } - public class MyLinqToHqlGeneratorsRegistry: DefaultLinqToHqlGeneratorsRegistry + public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { - public MyLinqToHqlGeneratorsRegistry():base() + public MyLinqToHqlGeneratorsRegistry() : base() { RegisterGenerator(ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)), new IsLikeGenerator()); - RegisterGenerator(ReflectHelper.GetMethodDefinition(() => new object().Equals(null)), new ObjectEqualsGenerator()); RegisterGenerator(ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.GetTime(default(DateTime))), new GetTimeGenerator()); } } @@ -58,10 +56,10 @@ public class IsLikeGenerator : BaseHqlGeneratorForMethod { public IsLikeGenerator() { - SupportedMethods = new[] {ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null))}; + SupportedMethods = new[] { ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)) }; } - public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, + public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(), @@ -69,21 +67,6 @@ public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, } } - public class ObjectEqualsGenerator : BaseHqlGeneratorForMethod - { - public ObjectEqualsGenerator() - { - SupportedMethods = new[] { ReflectHelper.GetMethodDefinition(() => new object().Equals(null)) }; - } - - public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, - ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) - { - return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), - visitor.Visit(arguments[0]).AsExpression()); - } - } - [TestFixture] public class CustomExtensionsExample : LinqTestCase { @@ -92,14 +75,6 @@ protected override void Configure(NHibernate.Cfg.Configuration configuration) configuration.LinqToHqlGeneratorsRegistry(); } - [Test] - public void CanUseObjectEquals() - { - var users = db.Users.Where(o => ((object) EnumStoredAsString.Medium).Equals(o.NullableEnum1)).ToList(); - Assert.That(users.Count, Is.EqualTo(2)); - Assert.That(users.All(c => c.NullableEnum1 == EnumStoredAsString.Medium), Is.True); - } - [Test(Description = "GH-2963")] public void CanUseComparisonWithExtensionOnMappedProperty() { diff --git a/src/NHibernate.Test/Linq/FunctionTests.cs b/src/NHibernate.Test/Linq/FunctionTests.cs index 51f70bd1f3d..b65c6ab85e9 100644 --- a/src/NHibernate.Test/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Linq/FunctionTests.cs @@ -507,6 +507,17 @@ where item.Gender.Equals(item.Gender) ObjectDumper.Write(query); } + + [Test] + public void WhereObjectEqual() + { + var query = from item in db.PatientRecords + where ((object) item.Gender).Equals(Gender.Female) + select item; + + ObjectDumper.Write(query); + } + [Test] public void WhereEquatableEqual() { diff --git a/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs b/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs deleted file mode 100644 index 92bf6b3ef87..00000000000 --- a/src/NHibernate/Linq/ExpressionTransformers/EnumEqualsTransformer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Linq.Expressions; -using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; - -namespace NHibernate.Linq.ExpressionTransformers -{ - /// - /// Transforms .Equals method to equality operator. - /// This cannot be done easily in as Equals operator - /// is boxed to ((object)Enum).Equals((object)EnumValue) expression. - /// - public class EnumEqualsTransformer : IExpressionTransformer - { - public ExpressionType[] SupportedExpressionTypes => _supportedExpressionTypes; - - private static readonly ExpressionType[] _supportedExpressionTypes = { ExpressionType.Call }; - - public Expression Transform(MethodCallExpression expression) - { - if (expression.Object?.Type.IsEnum == true && - expression.Method.Name == nameof(Enum.Equals) && - expression.Arguments.Count == 1) - { - return Expression.Equal(expression.Object, Unwrap(expression.Arguments[0], expression.Object.Type)); - } - - return expression; - } - - private Expression Unwrap(Expression expression, System.Type type) - { - // 1) unwrap convert operand as convert is converting from enum type to object - if (expression is UnaryExpression u && u.NodeType == ExpressionType.Convert) - { - return u.Operand; - } - - // 2) convert constant expression which is of type object - if (expression is ConstantExpression c) - { - return Expression.Convert(c, type); - } - - return expression; - } - } -} diff --git a/src/NHibernate/Linq/Functions/EqualsGenerator.cs b/src/NHibernate/Linq/Functions/EqualsGenerator.cs index 82c15c87190..301d405b1be 100644 --- a/src/NHibernate/Linq/Functions/EqualsGenerator.cs +++ b/src/NHibernate/Linq/Functions/EqualsGenerator.cs @@ -58,7 +58,10 @@ public class EqualsGenerator : BaseHqlGeneratorForMethod ReflectHelper.GetMethodDefinition>(x => x.Equals(default(DateTime))), ReflectHelper.GetMethodDefinition>(x => x.Equals(default(DateTimeOffset))), ReflectHelper.GetMethodDefinition>(x => x.Equals(default(TimeSpan))), - ReflectHelper.GetMethodDefinition>(x => x.Equals(default(bool))) + ReflectHelper.GetMethodDefinition>(x => x.Equals(default(bool))), + ReflectHelper.GetMethodDefinition(x => x.Equals(default(object))), // this covers also Enum.Equals + ReflectHelper.GetMethodDefinition>(x => x.Equals(default(object))), + ReflectHelper.GetMethodDefinition>(x => x.Equals(default(Enum))) }; public EqualsGenerator() @@ -72,7 +75,10 @@ public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, { Expression lhs = arguments.Count == 1 ? targetObject : arguments[0]; Expression rhs = arguments.Count == 1 ? arguments[0] : arguments[1]; - + if (lhs.Type.IsEnum) + { + return visitor.Visit(Expression.Equal(lhs, Expression.Convert(rhs, lhs.Type))); + } return visitor.Visit(Expression.Equal(lhs, rhs)); } } diff --git a/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs b/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs deleted file mode 100644 index c943894ee47..00000000000 --- a/src/NHibernate/Linq/NHibernateNodeTypeProvider.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using NHibernate.Util; -using Remotion.Linq.EagerFetching.Parsing; -using Remotion.Linq.Parsing.Structure; -using Remotion.Linq.Parsing.Structure.NodeTypeProviders; - -namespace NHibernate.Linq -{ - public class NHibernateNodeTypeProvider : INodeTypeProvider - { - private INodeTypeProvider defaultNodeTypeProvider; - - public NHibernateNodeTypeProvider() - { - var methodInfoRegistry = new MethodInfoBasedNodeTypeRegistry(); - - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.Fetch, default(IQueryable), default(Expression>)) }, - typeof(FetchOneExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchLazyProperties, default(IQueryable)) }, - typeof(FetchLazyPropertiesExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchMany, default(IQueryable), default(Expression>>)) }, - typeof(FetchManyExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetch, default(INhFetchRequest), default(Expression>)) }, - typeof(ThenFetchOneExpressionNode)); - methodInfoRegistry.Register( - new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, - typeof(ThenFetchManyExpressionNode)); - methodInfoRegistry.Register( - new[] - { - ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IQueryable), default(LockMode)), - ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IEnumerable), default(LockMode)) - }, - typeof(LockExpressionNode)); - - var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); - nodeTypeProvider.InnerProviders.Add(methodInfoRegistry); - defaultNodeTypeProvider = nodeTypeProvider; - } - - public bool IsRegistered(MethodInfo method) - { - // Avoid Relinq turning IDictionary.Contains into ContainsResultOperator. We do our own processing for that method. - if (method.DeclaringType == typeof(IDictionary) && method.Name == "Contains") - return false; - - return defaultNodeTypeProvider.IsRegistered(method); - } - - public System.Type GetNodeType(MethodInfo method) - { - return defaultNodeTypeProvider.GetNodeType(method); - } - } -} diff --git a/src/NHibernate/Linq/NhRelinqQueryParser.cs b/src/NHibernate/Linq/NhRelinqQueryParser.cs index a2c7e2f6c86..eba4a3d6283 100644 --- a/src/NHibernate/Linq/NhRelinqQueryParser.cs +++ b/src/NHibernate/Linq/NhRelinqQueryParser.cs @@ -1,12 +1,20 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; +using System.Reflection; +using NHibernate.Engine; using NHibernate.Linq.ExpressionTransformers; using NHibernate.Linq.Visitors; +using NHibernate.Param; +using NHibernate.Util; using Remotion.Linq; +using Remotion.Linq.EagerFetching.Parsing; using Remotion.Linq.Parsing.ExpressionVisitors.Transformation; using Remotion.Linq.Parsing.Structure; using Remotion.Linq.Parsing.Structure.ExpressionTreeProcessors; +using Remotion.Linq.Parsing.Structure.NodeTypeProviders; namespace NHibernate.Linq { @@ -19,7 +27,6 @@ static NhRelinqQueryParser() var transformerRegistry = ExpressionTransformerRegistry.CreateDefault(); transformerRegistry.Register(new RemoveRedundantCast()); transformerRegistry.Register(new SimplifyCompareTransformer()); - transformerRegistry.Register(new EnumEqualsTransformer()); // If needing a compound processor for adding other processing, do not use // ExpressionTreeParser.CreateDefaultProcessor(transformerRegistry), it would @@ -85,4 +92,55 @@ internal static Func CreatePreTransformer(IExpressionTra return new TransformingExpressionTreeProcessor(preTransformerRegistry).Process; } } + + public class NHibernateNodeTypeProvider : INodeTypeProvider + { + private INodeTypeProvider defaultNodeTypeProvider; + + public NHibernateNodeTypeProvider() + { + var methodInfoRegistry = new MethodInfoBasedNodeTypeRegistry(); + + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.Fetch, default(IQueryable), default(Expression>)) }, + typeof(FetchOneExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchLazyProperties, default(IQueryable)) }, + typeof(FetchLazyPropertiesExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.FetchMany, default(IQueryable), default(Expression>>)) }, + typeof(FetchManyExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition(EagerFetchingExtensionMethods.ThenFetch, default(INhFetchRequest), default(Expression>)) }, + typeof(ThenFetchOneExpressionNode)); + methodInfoRegistry.Register( + new[] { ReflectHelper.FastGetMethodDefinition( EagerFetchingExtensionMethods.ThenFetchMany, default(INhFetchRequest), default(Expression>>)) }, + typeof(ThenFetchManyExpressionNode)); + methodInfoRegistry.Register( + new[] + { + ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IQueryable), default(LockMode)), + ReflectHelper.FastGetMethodDefinition(LinqExtensionMethods.WithLock, default(IEnumerable), default(LockMode)) + }, + typeof(LockExpressionNode)); + + var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider(); + nodeTypeProvider.InnerProviders.Add(methodInfoRegistry); + defaultNodeTypeProvider = nodeTypeProvider; + } + + public bool IsRegistered(MethodInfo method) + { + // Avoid Relinq turning IDictionary.Contains into ContainsResultOperator. We do our own processing for that method. + if (method.DeclaringType == typeof(IDictionary) && method.Name == "Contains") + return false; + + return defaultNodeTypeProvider.IsRegistered(method); + } + + public System.Type GetNodeType(MethodInfo method) + { + return defaultNodeTypeProvider.GetNodeType(method); + } + } } From 5cfb1b2abcde9a70d559cf3105ebd811f7943192 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Mar 2023 07:32:51 +0000 Subject: [PATCH 07/15] Generate async files --- .../Async/Linq/CustomExtensionsExample.cs | 3 +-- src/NHibernate.Test/Async/Linq/FunctionTests.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs index 01141b2f240..a9b7975231d 100644 --- a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs @@ -14,7 +14,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; -using NHibernate.DomainModel.Northwind.Entities; using NHibernate.Hql.Ast; using NHibernate.Linq.Functions; using NHibernate.Linq.Visitors; @@ -32,7 +31,7 @@ public class CustomExtensionsExampleAsync : LinqTestCase protected override void Configure(NHibernate.Cfg.Configuration configuration) { configuration.LinqToHqlGeneratorsRegistry(); - } + } [Test(Description = "GH-2963")] public async Task CanUseComparisonWithExtensionOnMappedPropertyAsync() diff --git a/src/NHibernate.Test/Async/Linq/FunctionTests.cs b/src/NHibernate.Test/Async/Linq/FunctionTests.cs index 2c7906dfb85..f1a7c639d35 100644 --- a/src/NHibernate.Test/Async/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Async/Linq/FunctionTests.cs @@ -475,6 +475,17 @@ where item.Gender.Equals(item.Gender) await (ObjectDumper.WriteAsync(query)); } + + [Test] + public async Task WhereObjectEqualAsync() + { + var query = from item in db.PatientRecords + where ((object) item.Gender).Equals(Gender.Female) + select item; + + await (ObjectDumper.WriteAsync(query)); + } + [Test] public async Task WhereEquatableEqualAsync() { From 6fa619f4ddaa97a51eca77f9dede5253c332d272 Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Mon, 27 Mar 2023 09:34:37 +0200 Subject: [PATCH 08/15] fix source analyse test --- src/NHibernate.Test/Linq/CustomExtensionsExample.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs index 919bf6b5183..33545b6fca0 100644 --- a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs @@ -31,7 +31,7 @@ public static TimeSpan GetTime(this DateTime dateTime) public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { - public MyLinqToHqlGeneratorsRegistry() : base() + public MyLinqToHqlGeneratorsRegistry() { RegisterGenerator(ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)), new IsLikeGenerator()); From 35bc9f0cf4511aca224d761073a4718932162cd8 Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Mon, 27 Mar 2023 11:37:52 +0200 Subject: [PATCH 09/15] additional fix of object equality used in tests --- .../GH1879/ExpansionRegressionTests.cs | 31 +------------ .../GH1879/ExpansionRegressionTests.cs | 46 ++----------------- 2 files changed, 5 insertions(+), 72 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index 86c809a8c1d..4eec3aa3ff9 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -40,36 +40,7 @@ protected override void OnSetUp() session.Flush(); transaction.Commit(); } - } - - protected override void Configure(Configuration configuration) - { - configuration.LinqToHqlGeneratorsRegistry(); - } - - private class TestLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry - { - public TestLinqToHqlGeneratorsRegistry() - { - this.Merge(new ObjectEquality()); - } - } - - private class ObjectEquality : IHqlGeneratorForMethod - { - public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) - { - return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), visitor.Visit(arguments[0]).AsExpression()); - } - - public IEnumerable SupportedMethods - { - get - { - yield return ReflectHelper.GetMethodDefinition(x => x.Equals(x)); - } - } - } + } [Test] public async Task MethodShouldNotExpandForNonConditionalOrCoalesceAsync() diff --git a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index 834a06bea2a..aea80e18dd1 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -1,13 +1,4 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using NHibernate.Cfg; -using NHibernate.Hql.Ast; -using NHibernate.Linq.Functions; -using NHibernate.Linq.Visitors; -using NHibernate.Util; +using System.Linq; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.GH1879 @@ -30,41 +21,12 @@ protected override void OnSetUp() } } - protected override void Configure(Configuration configuration) - { - configuration.LinqToHqlGeneratorsRegistry(); - } - - private class TestLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry - { - public TestLinqToHqlGeneratorsRegistry() - { - this.Merge(new ObjectEquality()); - } - } - - private class ObjectEquality : IHqlGeneratorForMethod - { - public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) - { - return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), visitor.Visit(arguments[0]).AsExpression()); - } - - public IEnumerable SupportedMethods - { - get - { - yield return ReflectHelper.GetMethodDefinition(x => x.Equals(x)); - } - } - } - [Test] public void MethodShouldNotExpandForNonConditionalOrCoalesce() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object)(e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object) (e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); } } @@ -73,7 +35,7 @@ public void MethodShouldNotExpandForConditionalWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object)(e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object) (e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); } } @@ -82,7 +44,7 @@ public void MethodShouldNotExpandForCoalesceWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object)(e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object) (e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); } } } From abd5f6c95794975a19a68c1ff2a0d78f38f1b786 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Mar 2023 09:42:21 +0000 Subject: [PATCH 10/15] Generate async files --- .../GH1879/ExpansionRegressionTests.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index 4eec3aa3ff9..bbf72a9475e 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -8,16 +8,7 @@ //------------------------------------------------------------------------------ -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using NHibernate.Cfg; -using NHibernate.Hql.Ast; -using NHibernate.Linq.Functions; -using NHibernate.Linq.Visitors; -using NHibernate.Util; using NUnit.Framework; using NHibernate.Linq; @@ -40,14 +31,14 @@ protected override void OnSetUp() session.Flush(); transaction.Commit(); } - } + } [Test] public async Task MethodShouldNotExpandForNonConditionalOrCoalesceAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object)(e.Amount + e.SpecialAmount)).Equals(110))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object) (e.Amount + e.SpecialAmount)).Equals(110))), Is.EqualTo(2)); } } @@ -56,7 +47,7 @@ public async Task MethodShouldNotExpandForConditionalWithPropertyAccessorAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object)(e.Paid ? e.Amount : e.SpecialAmount)).Equals(10))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object) (e.Paid ? e.Amount : e.SpecialAmount)).Equals(10))), Is.EqualTo(2)); } } @@ -65,7 +56,7 @@ public async Task MethodShouldNotExpandForCoalesceWithPropertyAccessorAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object)(e.SpecialAmount ?? e.Amount)).Equals(100))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object) (e.SpecialAmount ?? e.Amount)).Equals(100))), Is.EqualTo(2)); } } } From e7913b988fab037706e04663012f118e836b405a Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Sat, 1 Apr 2023 15:06:45 +0200 Subject: [PATCH 11/15] remove unnecessary code formatting --- .../Linq/CustomExtensionsExample.cs | 7 +- src/NHibernate.Test/Linq/FunctionTests.cs | 92 +++++++++---------- .../GH1879/ExpansionRegressionTests.cs | 6 +- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs index 33545b6fca0..2c0ae5f178e 100644 --- a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs @@ -4,6 +4,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; +using NHibernate.DomainModel.Northwind.Entities; using NHibernate.Hql.Ast; using NHibernate.Linq.Functions; using NHibernate.Linq.Visitors; @@ -18,7 +19,7 @@ public static bool IsLike(this string source, string pattern) { pattern = Regex.Escape(pattern); pattern = pattern.Replace("%", ".*?").Replace("_", "."); - pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^"); + pattern = pattern.Replace(@"\[", "[").Replace(@"\]","]").Replace(@"\^", "^"); return Regex.IsMatch(source, pattern); } @@ -56,10 +57,10 @@ public class IsLikeGenerator : BaseHqlGeneratorForMethod { public IsLikeGenerator() { - SupportedMethods = new[] { ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)) }; + SupportedMethods = new[] {ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null))}; } - public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, + public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(), diff --git a/src/NHibernate.Test/Linq/FunctionTests.cs b/src/NHibernate.Test/Linq/FunctionTests.cs index b65c6ab85e9..877040e35ba 100644 --- a/src/NHibernate.Test/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Linq/FunctionTests.cs @@ -36,8 +36,8 @@ public void LikeFunctionWithEscapeCharacter() session.Flush(); var query = (from e in db.Employees - where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) - select e).ToList(); + where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo(employeeName)); @@ -81,8 +81,8 @@ where NHibernate.Test.Linq.FunctionTests.SqlMethods.Like(e.FirstName, "Ma%et") public void SubstringFunction2() { var query = (from e in db.Employees - where e.FirstName.Substring(0, 2) == "An" - select e).ToList(); + where e.FirstName.Substring(0, 2) == "An" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(2)); } @@ -91,8 +91,8 @@ where e.FirstName.Substring(0, 2) == "An" public void SubstringFunction1() { var query = (from e in db.Employees - where e.FirstName.Substring(3) == "rew" - select e).ToList(); + where e.FirstName.Substring(3) == "rew" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo("Andrew")); @@ -130,21 +130,21 @@ public void ReplaceFunction() var query = from e in db.Employees where e.FirstName.StartsWith("An") select new - { - Before = e.FirstName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethod = e.FirstName.Replace("An", "Zan"), - AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), - AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), + { + Before = e.FirstName, + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethod = e.FirstName.Replace("An", "Zan"), + AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), + AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), BeforeConst = suppliedName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethodConst = suppliedName.Replace("An", "Zan"), - AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), - AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethodConst = suppliedName.Replace("An", "Zan"), + AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), + AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") }; var results = query.ToList(); var s = ObjectDumper.Write(results); @@ -171,12 +171,12 @@ where e.FirstName.StartsWith("An") // Should cause ReplaceWithEvaluation to fail suppliedName = null; var failingQuery = from e in db.Employees - where e.FirstName.StartsWith("An") - select new - { - Before = e.FirstName, - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") - }; + where e.FirstName.StartsWith("An") + select new + { + Before = e.FirstName, + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") + }; Assert.That(() => failingQuery.ToList(), Throws.InstanceOf().And.InnerException.InstanceOf()); } @@ -248,7 +248,7 @@ where lowerName.Contains("a") public void TwoFunctionExpression() { var query = from e in db.Employees - where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month + where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month select e.FirstName; ObjectDumper.Write(query); @@ -285,9 +285,9 @@ public void Trim() { using (session.BeginTransaction()) { - AnotherEntity ae1 = new AnotherEntity { Input = " hi " }; - AnotherEntity ae2 = new AnotherEntity { Input = "hi" }; - AnotherEntity ae3 = new AnotherEntity { Input = "heh" }; + AnotherEntity ae1 = new AnotherEntity {Input = " hi "}; + AnotherEntity ae2 = new AnotherEntity {Input = "hi"}; + AnotherEntity ae3 = new AnotherEntity {Input = "heh"}; session.Save(ae1); session.Save(ae2); session.Save(ae3); @@ -303,7 +303,7 @@ public void Trim() // Check when passed as array // (the single character parameter is a new overload in .netcoreapp2.0, but not net461 or .netstandard2.0). - Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new[] { 'h' }) == "e")); + Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new [] { 'h' }) == "e")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimStart(new[] { 'h' }) == "eh")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimEnd(new[] { 'h' }) == "he")); @@ -316,9 +316,9 @@ public void TrimInitialWhitespace() { using (session.BeginTransaction()) { - session.Save(new AnotherEntity { Input = " hi" }); - session.Save(new AnotherEntity { Input = "hi" }); - session.Save(new AnotherEntity { Input = "heh" }); + session.Save(new AnotherEntity {Input = " hi"}); + session.Save(new AnotherEntity {Input = "hi"}); + session.Save(new AnotherEntity {Input = "heh"}); session.Flush(); Assert.That(session.Query().Count(e => e.Input.TrimStart() == "hi"), Is.EqualTo(2)); @@ -372,7 +372,7 @@ public void WhereBoolConstantEqual() var query = from item in db.Role where item.IsActive.Equals(true) select item; - + ObjectDumper.Write(query); } @@ -382,7 +382,7 @@ public void WhereBoolConditionEquals() var query = from item in db.Role where item.IsActive.Equals(item.Name != null) select item; - + ObjectDumper.Write(query); } @@ -392,7 +392,7 @@ public void WhereBoolParameterEqual() var query = from item in db.Role where item.IsActive.Equals(1 == 1) select item; - + ObjectDumper.Write(query); } @@ -412,8 +412,8 @@ where item.IsActive.Equals(f()) public void WhereLongEqual() { var query = from item in db.PatientRecords - where item.Id.Equals(-1) - select item; + where item.Id.Equals(-1) + select item; ObjectDumper.Write(query); } @@ -427,7 +427,7 @@ where item.RegisteredAt.Equals(DateTime.Today) ObjectDumper.Write(query); } - + [Test] public void WhereGuidEqual() { @@ -436,7 +436,7 @@ where item.Reference.Equals(Guid.Empty) select item; ObjectDumper.Write(query); - } + } [Test] public void WhereDoubleEqual() @@ -446,8 +446,8 @@ where item.BodyWeight.Equals(-1) select item; ObjectDumper.Write(query); - } - + } + [Test] [Ignore("Not mapped entity")] public void WhereFloatEqual() @@ -457,7 +457,7 @@ where item.Float.Equals(-1) select item; ObjectDumper.Write(query); - } + } [Test] [Ignore("Not mapped entity")] @@ -522,8 +522,8 @@ public void WhereObjectEqual() public void WhereEquatableEqual() { var query = from item in db.Shippers - where ((IEquatable) item.Reference).Equals(Guid.Empty) - select item; + where ((IEquatable) item.Reference).Equals(Guid.Empty) + select item; ObjectDumper.Write(query); } diff --git a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index aea80e18dd1..1686e089be8 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -26,7 +26,7 @@ public void MethodShouldNotExpandForNonConditionalOrCoalesce() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); } } @@ -35,7 +35,7 @@ public void MethodShouldNotExpandForConditionalWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); } } @@ -44,7 +44,7 @@ public void MethodShouldNotExpandForCoalesceWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); } } } From 328d626852142c3c235d7d332dbac9f2d78ca854 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 1 Apr 2023 13:14:07 +0000 Subject: [PATCH 12/15] Generate async files --- .../Async/Linq/CustomExtensionsExample.cs | 1 + .../Async/Linq/FunctionTests.cs | 88 +++++++++---------- .../GH1879/ExpansionRegressionTests.cs | 6 +- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs index a9b7975231d..b1a0d8299ec 100644 --- a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs @@ -14,6 +14,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; +using NHibernate.DomainModel.Northwind.Entities; using NHibernate.Hql.Ast; using NHibernate.Linq.Functions; using NHibernate.Linq.Visitors; diff --git a/src/NHibernate.Test/Async/Linq/FunctionTests.cs b/src/NHibernate.Test/Async/Linq/FunctionTests.cs index f1a7c639d35..c39ff901df4 100644 --- a/src/NHibernate.Test/Async/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Async/Linq/FunctionTests.cs @@ -48,8 +48,8 @@ public async Task LikeFunctionWithEscapeCharacterAsync() await (session.FlushAsync()); var query = await ((from e in db.Employees - where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) - select e).ToListAsync()); + where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo(employeeName)); @@ -93,8 +93,8 @@ where NHibernate.Test.Linq.FunctionTestsAsync.SqlMethods.Like(e.FirstName, "Ma%e public async Task SubstringFunction2Async() { var query = await ((from e in db.Employees - where e.FirstName.Substring(0, 2) == "An" - select e).ToListAsync()); + where e.FirstName.Substring(0, 2) == "An" + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(2)); } @@ -103,8 +103,8 @@ where e.FirstName.Substring(0, 2) == "An" public async Task SubstringFunction1Async() { var query = await ((from e in db.Employees - where e.FirstName.Substring(3) == "rew" - select e).ToListAsync()); + where e.FirstName.Substring(3) == "rew" + select e).ToListAsync()); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo("Andrew")); @@ -142,21 +142,21 @@ public async Task ReplaceFunctionAsync() var query = from e in db.Employees where e.FirstName.StartsWith("An") select new - { - Before = e.FirstName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethod = e.FirstName.Replace("An", "Zan"), - AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), - AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), + { + Before = e.FirstName, + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethod = e.FirstName.Replace("An", "Zan"), + AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), + AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), BeforeConst = suppliedName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethodConst = suppliedName.Replace("An", "Zan"), - AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), - AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethodConst = suppliedName.Replace("An", "Zan"), + AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), + AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") }; var results = await (query.ToListAsync()); var s = await (ObjectDumper.WriteAsync(results)); @@ -183,12 +183,12 @@ where e.FirstName.StartsWith("An") // Should cause ReplaceWithEvaluation to fail suppliedName = null; var failingQuery = from e in db.Employees - where e.FirstName.StartsWith("An") - select new - { - Before = e.FirstName, - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") - }; + where e.FirstName.StartsWith("An") + select new + { + Before = e.FirstName, + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") + }; Assert.That(() => failingQuery.ToListAsync(), Throws.InstanceOf().And.InnerException.InstanceOf()); } @@ -260,7 +260,7 @@ where lowerName.Contains("a") public async Task TwoFunctionExpressionAsync() { var query = from e in db.Employees - where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month + where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month select e.FirstName; await (ObjectDumper.WriteAsync(query)); @@ -297,9 +297,9 @@ public async Task TrimAsync() { using (session.BeginTransaction()) { - AnotherEntity ae1 = new AnotherEntity { Input = " hi " }; - AnotherEntity ae2 = new AnotherEntity { Input = "hi" }; - AnotherEntity ae3 = new AnotherEntity { Input = "heh" }; + AnotherEntity ae1 = new AnotherEntity {Input = " hi "}; + AnotherEntity ae2 = new AnotherEntity {Input = "hi"}; + AnotherEntity ae3 = new AnotherEntity {Input = "heh"}; await (session.SaveAsync(ae1)); await (session.SaveAsync(ae2)); await (session.SaveAsync(ae3)); @@ -315,7 +315,7 @@ public async Task TrimAsync() // Check when passed as array // (the single character parameter is a new overload in .netcoreapp2.0, but not net461 or .netstandard2.0). - Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.Trim(new[] { 'h' }) == "e"))); + Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.Trim(new [] { 'h' }) == "e"))); Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.TrimStart(new[] { 'h' }) == "eh"))); Assert.AreEqual(1, await (session.Query().CountAsync(e => e.Input.TrimEnd(new[] { 'h' }) == "he"))); @@ -328,9 +328,9 @@ public async Task TrimInitialWhitespaceAsync() { using (session.BeginTransaction()) { - await (session.SaveAsync(new AnotherEntity { Input = " hi" })); - await (session.SaveAsync(new AnotherEntity { Input = "hi" })); - await (session.SaveAsync(new AnotherEntity { Input = "heh" })); + await (session.SaveAsync(new AnotherEntity {Input = " hi"})); + await (session.SaveAsync(new AnotherEntity {Input = "hi"})); + await (session.SaveAsync(new AnotherEntity {Input = "heh"})); await (session.FlushAsync()); Assert.That(await (session.Query().CountAsync(e => e.Input.TrimStart() == "hi")), Is.EqualTo(2)); @@ -373,7 +373,7 @@ public async Task WhereBoolConstantEqualAsync() var query = from item in db.Role where item.IsActive.Equals(true) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -383,7 +383,7 @@ public async Task WhereBoolConditionEqualsAsync() var query = from item in db.Role where item.IsActive.Equals(item.Name != null) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -393,7 +393,7 @@ public async Task WhereBoolParameterEqualAsync() var query = from item in db.Role where item.IsActive.Equals(1 == 1) select item; - + await (ObjectDumper.WriteAsync(query)); } @@ -413,8 +413,8 @@ where item.IsActive.Equals(f()) public async Task WhereLongEqualAsync() { var query = from item in db.PatientRecords - where item.Id.Equals(-1) - select item; + where item.Id.Equals(-1) + select item; await (ObjectDumper.WriteAsync(query)); } @@ -428,7 +428,7 @@ where item.RegisteredAt.Equals(DateTime.Today) await (ObjectDumper.WriteAsync(query)); } - + [Test] public async Task WhereGuidEqualAsync() { @@ -437,7 +437,7 @@ where item.Reference.Equals(Guid.Empty) select item; await (ObjectDumper.WriteAsync(query)); - } + } [Test] public async Task WhereDoubleEqualAsync() @@ -447,7 +447,7 @@ where item.BodyWeight.Equals(-1) select item; await (ObjectDumper.WriteAsync(query)); - } + } [Test] public async Task WhereDecimalEqualAsync() @@ -490,8 +490,8 @@ public async Task WhereObjectEqualAsync() public async Task WhereEquatableEqualAsync() { var query = from item in db.Shippers - where ((IEquatable) item.Reference).Equals(Guid.Empty) - select item; + where ((IEquatable) item.Reference).Equals(Guid.Empty) + select item; await (ObjectDumper.WriteAsync(query)); } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index bbf72a9475e..a74fdcbe2d1 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -38,7 +38,7 @@ public async Task MethodShouldNotExpandForNonConditionalOrCoalesceAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object) (e.Amount + e.SpecialAmount)).Equals(110))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object)(e.Amount + e.SpecialAmount)).Equals(110))), Is.EqualTo(2)); } } @@ -47,7 +47,7 @@ public async Task MethodShouldNotExpandForConditionalWithPropertyAccessorAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object) (e.Paid ? e.Amount : e.SpecialAmount)).Equals(10))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object)(e.Paid ? e.Amount : e.SpecialAmount)).Equals(10))), Is.EqualTo(2)); } } @@ -56,7 +56,7 @@ public async Task MethodShouldNotExpandForCoalesceWithPropertyAccessorAsync() { using (var session = OpenSession()) { - Assert.That(await (session.Query().CountAsync(e => ((object) (e.SpecialAmount ?? e.Amount)).Equals(100))), Is.EqualTo(2)); + Assert.That(await (session.Query().CountAsync(e => ((object)(e.SpecialAmount ?? e.Amount)).Equals(100))), Is.EqualTo(2)); } } } From ee0225a783fa6ac0fa05c34223872804bc8c1070 Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Sat, 1 Apr 2023 15:06:45 +0200 Subject: [PATCH 13/15] remove unnecessary code formatting --- .../Linq/CustomExtensionsExample.cs | 7 +- src/NHibernate.Test/Linq/FunctionTests.cs | 92 +++++++++---------- .../GH1879/ExpansionRegressionTests.cs | 6 +- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs index 33545b6fca0..2c0ae5f178e 100644 --- a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs @@ -4,6 +4,7 @@ using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; +using NHibernate.DomainModel.Northwind.Entities; using NHibernate.Hql.Ast; using NHibernate.Linq.Functions; using NHibernate.Linq.Visitors; @@ -18,7 +19,7 @@ public static bool IsLike(this string source, string pattern) { pattern = Regex.Escape(pattern); pattern = pattern.Replace("%", ".*?").Replace("_", "."); - pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^"); + pattern = pattern.Replace(@"\[", "[").Replace(@"\]","]").Replace(@"\^", "^"); return Regex.IsMatch(source, pattern); } @@ -56,10 +57,10 @@ public class IsLikeGenerator : BaseHqlGeneratorForMethod { public IsLikeGenerator() { - SupportedMethods = new[] { ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)) }; + SupportedMethods = new[] {ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null))}; } - public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, + public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(), diff --git a/src/NHibernate.Test/Linq/FunctionTests.cs b/src/NHibernate.Test/Linq/FunctionTests.cs index b65c6ab85e9..877040e35ba 100644 --- a/src/NHibernate.Test/Linq/FunctionTests.cs +++ b/src/NHibernate.Test/Linq/FunctionTests.cs @@ -36,8 +36,8 @@ public void LikeFunctionWithEscapeCharacter() session.Flush(); var query = (from e in db.Employees - where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) - select e).ToList(); + where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar) + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo(employeeName)); @@ -81,8 +81,8 @@ where NHibernate.Test.Linq.FunctionTests.SqlMethods.Like(e.FirstName, "Ma%et") public void SubstringFunction2() { var query = (from e in db.Employees - where e.FirstName.Substring(0, 2) == "An" - select e).ToList(); + where e.FirstName.Substring(0, 2) == "An" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(2)); } @@ -91,8 +91,8 @@ where e.FirstName.Substring(0, 2) == "An" public void SubstringFunction1() { var query = (from e in db.Employees - where e.FirstName.Substring(3) == "rew" - select e).ToList(); + where e.FirstName.Substring(3) == "rew" + select e).ToList(); Assert.That(query.Count, Is.EqualTo(1)); Assert.That(query[0].FirstName, Is.EqualTo("Andrew")); @@ -130,21 +130,21 @@ public void ReplaceFunction() var query = from e in db.Employees where e.FirstName.StartsWith("An") select new - { - Before = e.FirstName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethod = e.FirstName.Replace("An", "Zan"), - AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), - AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), + { + Before = e.FirstName, + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethod = e.FirstName.Replace("An", "Zan"), + AfterExtension = ExtensionMethods.Replace(e.FirstName, "An", "Zan"), + AfterNamedExtension = e.FirstName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtension = e.FirstName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2Extension = e.FirstName.ReplaceWithEvaluation2("An", "Zan"), BeforeConst = suppliedName, - // This one call the standard string.Replace, not the extension. The linq registry handles it. - AfterMethodConst = suppliedName.Replace("An", "Zan"), - AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), - AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), - AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") + // This one call the standard string.Replace, not the extension. The linq registry handles it. + AfterMethodConst = suppliedName.Replace("An", "Zan"), + AfterExtensionConst = ExtensionMethods.Replace(suppliedName, "An", "Zan"), + AfterNamedExtensionConst = suppliedName.ReplaceExtension("An", "Zan"), + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan"), + AfterEvaluable2ExtensionConst = suppliedName.ReplaceWithEvaluation2("An", "Zan") }; var results = query.ToList(); var s = ObjectDumper.Write(results); @@ -171,12 +171,12 @@ where e.FirstName.StartsWith("An") // Should cause ReplaceWithEvaluation to fail suppliedName = null; var failingQuery = from e in db.Employees - where e.FirstName.StartsWith("An") - select new - { - Before = e.FirstName, - AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") - }; + where e.FirstName.StartsWith("An") + select new + { + Before = e.FirstName, + AfterEvaluableExtensionConst = suppliedName.ReplaceWithEvaluation("An", "Zan") + }; Assert.That(() => failingQuery.ToList(), Throws.InstanceOf().And.InnerException.InstanceOf()); } @@ -248,7 +248,7 @@ where lowerName.Contains("a") public void TwoFunctionExpression() { var query = from e in db.Employees - where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month + where e.FirstName.IndexOf("A") == e.BirthDate.Value.Month select e.FirstName; ObjectDumper.Write(query); @@ -285,9 +285,9 @@ public void Trim() { using (session.BeginTransaction()) { - AnotherEntity ae1 = new AnotherEntity { Input = " hi " }; - AnotherEntity ae2 = new AnotherEntity { Input = "hi" }; - AnotherEntity ae3 = new AnotherEntity { Input = "heh" }; + AnotherEntity ae1 = new AnotherEntity {Input = " hi "}; + AnotherEntity ae2 = new AnotherEntity {Input = "hi"}; + AnotherEntity ae3 = new AnotherEntity {Input = "heh"}; session.Save(ae1); session.Save(ae2); session.Save(ae3); @@ -303,7 +303,7 @@ public void Trim() // Check when passed as array // (the single character parameter is a new overload in .netcoreapp2.0, but not net461 or .netstandard2.0). - Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new[] { 'h' }) == "e")); + Assert.AreEqual(1, session.Query().Count(e => e.Input.Trim(new [] { 'h' }) == "e")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimStart(new[] { 'h' }) == "eh")); Assert.AreEqual(1, session.Query().Count(e => e.Input.TrimEnd(new[] { 'h' }) == "he")); @@ -316,9 +316,9 @@ public void TrimInitialWhitespace() { using (session.BeginTransaction()) { - session.Save(new AnotherEntity { Input = " hi" }); - session.Save(new AnotherEntity { Input = "hi" }); - session.Save(new AnotherEntity { Input = "heh" }); + session.Save(new AnotherEntity {Input = " hi"}); + session.Save(new AnotherEntity {Input = "hi"}); + session.Save(new AnotherEntity {Input = "heh"}); session.Flush(); Assert.That(session.Query().Count(e => e.Input.TrimStart() == "hi"), Is.EqualTo(2)); @@ -372,7 +372,7 @@ public void WhereBoolConstantEqual() var query = from item in db.Role where item.IsActive.Equals(true) select item; - + ObjectDumper.Write(query); } @@ -382,7 +382,7 @@ public void WhereBoolConditionEquals() var query = from item in db.Role where item.IsActive.Equals(item.Name != null) select item; - + ObjectDumper.Write(query); } @@ -392,7 +392,7 @@ public void WhereBoolParameterEqual() var query = from item in db.Role where item.IsActive.Equals(1 == 1) select item; - + ObjectDumper.Write(query); } @@ -412,8 +412,8 @@ where item.IsActive.Equals(f()) public void WhereLongEqual() { var query = from item in db.PatientRecords - where item.Id.Equals(-1) - select item; + where item.Id.Equals(-1) + select item; ObjectDumper.Write(query); } @@ -427,7 +427,7 @@ where item.RegisteredAt.Equals(DateTime.Today) ObjectDumper.Write(query); } - + [Test] public void WhereGuidEqual() { @@ -436,7 +436,7 @@ where item.Reference.Equals(Guid.Empty) select item; ObjectDumper.Write(query); - } + } [Test] public void WhereDoubleEqual() @@ -446,8 +446,8 @@ where item.BodyWeight.Equals(-1) select item; ObjectDumper.Write(query); - } - + } + [Test] [Ignore("Not mapped entity")] public void WhereFloatEqual() @@ -457,7 +457,7 @@ where item.Float.Equals(-1) select item; ObjectDumper.Write(query); - } + } [Test] [Ignore("Not mapped entity")] @@ -522,8 +522,8 @@ public void WhereObjectEqual() public void WhereEquatableEqual() { var query = from item in db.Shippers - where ((IEquatable) item.Reference).Equals(Guid.Empty) - select item; + where ((IEquatable) item.Reference).Equals(Guid.Empty) + select item; ObjectDumper.Write(query); } diff --git a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index aea80e18dd1..1686e089be8 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -26,7 +26,7 @@ public void MethodShouldNotExpandForNonConditionalOrCoalesce() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.Amount + e.SpecialAmount)).Equals(110)), Is.EqualTo(2)); } } @@ -35,7 +35,7 @@ public void MethodShouldNotExpandForConditionalWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.Paid ? e.Amount : e.SpecialAmount)).Equals(10)), Is.EqualTo(2)); } } @@ -44,7 +44,7 @@ public void MethodShouldNotExpandForCoalesceWithPropertyAccessor() { using (var session = OpenSession()) { - Assert.That(session.Query().Count(e => ((object) (e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); + Assert.That(session.Query().Count(e => ((object)(e.SpecialAmount ?? e.Amount)).Equals(100)), Is.EqualTo(2)); } } } From 09f9a8f0b3e1bcf74dffeb0c25921c0ed72199e2 Mon Sep 17 00:00:00 2001 From: Tomas Lukac Date: Fri, 12 May 2023 09:43:58 +0200 Subject: [PATCH 14/15] revert ObjectEquality tests, change behavior of generators registry registering from add to add or update --- .../Linq/CustomExtensionsExample.cs | 28 ++++++++++++- .../GH1879/ExpansionRegressionTests.cs | 40 ++++++++++++++++++- .../DefaultLinqToHqlGeneratorsRegistry.cs | 4 +- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs index 2c0ae5f178e..58e6a5e0f4d 100644 --- a/src/NHibernate.Test/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Linq/CustomExtensionsExample.cs @@ -30,12 +30,13 @@ public static TimeSpan GetTime(this DateTime dateTime) } } - public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry + public class MyLinqToHqlGeneratorsRegistry: DefaultLinqToHqlGeneratorsRegistry { - public MyLinqToHqlGeneratorsRegistry() + public MyLinqToHqlGeneratorsRegistry():base() { RegisterGenerator(ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.IsLike(null, null)), new IsLikeGenerator()); + RegisterGenerator(ReflectHelper.GetMethodDefinition(() => new object().Equals(null)), new ObjectEqualsGenerator()); RegisterGenerator(ReflectHelper.GetMethodDefinition(() => MyLinqExtensions.GetTime(default(DateTime))), new GetTimeGenerator()); } } @@ -68,6 +69,21 @@ public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, } } + public class ObjectEqualsGenerator : BaseHqlGeneratorForMethod + { + public ObjectEqualsGenerator() + { + SupportedMethods = new[] { ReflectHelper.GetMethodDefinition(() => new object().Equals(null)) }; + } + + public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, + ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) + { + return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), + visitor.Visit(arguments[0]).AsExpression()); + } + } + [TestFixture] public class CustomExtensionsExample : LinqTestCase { @@ -76,6 +92,14 @@ protected override void Configure(NHibernate.Cfg.Configuration configuration) configuration.LinqToHqlGeneratorsRegistry(); } + [Test] + public void CanUseObjectEquals() + { + var users = db.Users.Where(o => ((object) EnumStoredAsString.Medium).Equals(o.NullableEnum1)).ToList(); + Assert.That(users.Count, Is.EqualTo(2)); + Assert.That(users.All(c => c.NullableEnum1 == EnumStoredAsString.Medium), Is.True); + } + [Test(Description = "GH-2963")] public void CanUseComparisonWithExtensionOnMappedProperty() { diff --git a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index 1686e089be8..834a06bea2a 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -1,4 +1,13 @@ -using System.Linq; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using NHibernate.Cfg; +using NHibernate.Hql.Ast; +using NHibernate.Linq.Functions; +using NHibernate.Linq.Visitors; +using NHibernate.Util; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.GH1879 @@ -21,6 +30,35 @@ protected override void OnSetUp() } } + protected override void Configure(Configuration configuration) + { + configuration.LinqToHqlGeneratorsRegistry(); + } + + private class TestLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry + { + public TestLinqToHqlGeneratorsRegistry() + { + this.Merge(new ObjectEquality()); + } + } + + private class ObjectEquality : IHqlGeneratorForMethod + { + public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) + { + return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), visitor.Visit(arguments[0]).AsExpression()); + } + + public IEnumerable SupportedMethods + { + get + { + yield return ReflectHelper.GetMethodDefinition(x => x.Equals(x)); + } + } + } + [Test] public void MethodShouldNotExpandForNonConditionalOrCoalesce() { diff --git a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs index 29595877d9f..9fe31b0a5bb 100644 --- a/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs +++ b/src/NHibernate/Linq/Functions/DefaultLinqToHqlGeneratorsRegistry.cs @@ -110,12 +110,12 @@ public virtual bool TryGetGenerator(MemberInfo property, out IHqlGeneratorForPro public virtual void RegisterGenerator(MethodInfo method, IHqlGeneratorForMethod generator) { - registeredMethods.Add(method, generator); + registeredMethods[method] = generator; } public virtual void RegisterGenerator(MemberInfo property, IHqlGeneratorForProperty generator) { - registeredProperties.Add(property, generator); + registeredProperties[property] = generator; } public void RegisterGenerator(IRuntimeMethodHqlGenerator generator) From 32c1623d6c0cf02f36a227967a68251725322a96 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 12 May 2023 07:47:43 +0000 Subject: [PATCH 15/15] Generate async files --- .../Async/Linq/CustomExtensionsExample.cs | 8 ++++ .../GH1879/ExpansionRegressionTests.cs | 38 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs index b1a0d8299ec..e1d32918feb 100644 --- a/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs +++ b/src/NHibernate.Test/Async/Linq/CustomExtensionsExample.cs @@ -34,6 +34,14 @@ protected override void Configure(NHibernate.Cfg.Configuration configuration) configuration.LinqToHqlGeneratorsRegistry(); } + [Test] + public async Task CanUseObjectEqualsAsync() + { + var users = await (db.Users.Where(o => ((object) EnumStoredAsString.Medium).Equals(o.NullableEnum1)).ToListAsync()); + Assert.That(users.Count, Is.EqualTo(2)); + Assert.That(users.All(c => c.NullableEnum1 == EnumStoredAsString.Medium), Is.True); + } + [Test(Description = "GH-2963")] public async Task CanUseComparisonWithExtensionOnMappedPropertyAsync() { diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs index a74fdcbe2d1..86c809a8c1d 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1879/ExpansionRegressionTests.cs @@ -8,7 +8,16 @@ //------------------------------------------------------------------------------ +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using NHibernate.Cfg; +using NHibernate.Hql.Ast; +using NHibernate.Linq.Functions; +using NHibernate.Linq.Visitors; +using NHibernate.Util; using NUnit.Framework; using NHibernate.Linq; @@ -33,6 +42,35 @@ protected override void OnSetUp() } } + protected override void Configure(Configuration configuration) + { + configuration.LinqToHqlGeneratorsRegistry(); + } + + private class TestLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry + { + public TestLinqToHqlGeneratorsRegistry() + { + this.Merge(new ObjectEquality()); + } + } + + private class ObjectEquality : IHqlGeneratorForMethod + { + public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) + { + return treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), visitor.Visit(arguments[0]).AsExpression()); + } + + public IEnumerable SupportedMethods + { + get + { + yield return ReflectHelper.GetMethodDefinition(x => x.Equals(x)); + } + } + } + [Test] public async Task MethodShouldNotExpandForNonConditionalOrCoalesceAsync() {