From 15aeb2a1a1257bcb57c0981c4d7a5dad0770d985 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Tue, 27 Apr 2021 12:51:29 -0400 Subject: [PATCH] support for method calls on string, this would help database drivers like mongo or even other database that have a string collation that is case sensitive. --- .../SimpleQueriesTest.cs | 64 +++++++++++++++++++ .../Helpers/QueryableHelpers.cs | 29 ++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/PoweredSoft.DynamicLinq.Test/SimpleQueriesTest.cs b/PoweredSoft.DynamicLinq.Test/SimpleQueriesTest.cs index 53bc020..b87c2a4 100644 --- a/PoweredSoft.DynamicLinq.Test/SimpleQueriesTest.cs +++ b/PoweredSoft.DynamicLinq.Test/SimpleQueriesTest.cs @@ -30,6 +30,70 @@ namespace PoweredSoft.DynamicLinq.Test Assert.IsTrue(newQuery.Any(), "Must have at least one author that matches"); } + [TestMethod] + public void EqualLowerCase() + { + // subject. + var authors = new List() + { + new Author { Id = long.MaxValue, FirstName = "David", LastName = "Lebee" } + }; + + // the query. + var query = authors.AsQueryable(); + + // simple where. + var newQuery = query.Where("FirstName.ToLower()", ConditionOperators.Equal, "david"); + + // must match. + Assert.IsTrue(newQuery.Any(), "Must have at least one author that matches"); + } + + [TestMethod] + public void EqualLowerCaseNullCheck() + { + // subject. + var authors = new List() + { + new Author { Id = long.MaxValue, FirstName = null, LastName = "Lebee" }, + new Author { Id = long.MaxValue, FirstName = "David", LastName = "Lebee" }, + }; + + // the query. + var query = authors.AsQueryable(); + + // simple where. + var newQuery = query.Where(wb => + { + wb.Equal("FirstName.ToLower()", "david").NullChecking(true); + }); + + // must match. + Assert.IsTrue(newQuery.Any(), "Must have at least one author that matches"); + } + + [TestMethod] + public void DoubleMethodCheck() + { + // subject. + var authors = new List() + { + new Author { Id = long.MaxValue, FirstName = "David ", LastName = "Lebee" }, + }; + + // the query. + var query = authors.AsQueryable(); + + // simple where. + var newQuery = query.Where(wb => + { + wb.Equal("FirstName.Trim().ToLower()", "david").NullChecking(true); + }); + + // must match. + Assert.IsTrue(newQuery.Any(), "Must have at least one author that matches"); + } + [TestMethod] public void Contains() { diff --git a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs index dc0253f..3109a0b 100644 --- a/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs +++ b/PoweredSoft.DynamicLinq/Helpers/QueryableHelpers.cs @@ -3,6 +3,7 @@ using PoweredSoft.DynamicLinq.Parser; using PoweredSoft.DynamicLinq.Resolver; using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -471,8 +472,18 @@ namespace PoweredSoft.DynamicLinq.Helpers var partStr = parts.First(); var isLast = parts.Count == 1; - // the member expression. - var memberExpression = Expression.PropertyOrField(current, partStr); + Expression memberExpression; + if (current != null && current.Type == typeof(string) && partStr.Contains("()")) + { + var finalMethodName = partStr.Replace("()", string.Empty); + var callingMethod = GetStringCallingMethod(finalMethodName); //typeof(string).GetMethod(finalMethodName, new Type[0]); + memberExpression = Expression.Call(current, callingMethod); + } + else + { + // the member expression. + memberExpression = Expression.PropertyOrField(current, partStr); + } // TODO : maybe support that last part is collection but what do we do? // not supported yet. @@ -535,7 +546,19 @@ namespace PoweredSoft.DynamicLinq.Helpers } } - public static Expression InAndNotIn(ParameterExpression parameter, ConditionOperators condition, object value, QueryConvertStrategy convertStrategy, MemberExpression memberExpression) + private static ConcurrentDictionary _stringMethodCache = new ConcurrentDictionary(); + private static MethodInfo GetStringCallingMethod(string methodName) + { + if (methodName == null) + throw new ArgumentNullException(nameof(methodName)); + + return _stringMethodCache.GetOrAdd(methodName, mn => + { + return typeof(string).GetMethod(mn, new Type[0]); + }); + } + + public static Expression InAndNotIn(ParameterExpression parameter, ConditionOperators condition, object value, QueryConvertStrategy convertStrategy, Expression memberExpression) { var enumerableValue = value as IEnumerable; if (enumerableValue == null)