Skip to content

Commit

Permalink
Add supported types dictionary for searching.
Browse files Browse the repository at this point in the history
  • Loading branch information
NazarovMikhail committed Jan 12, 2024
1 parent 9f18e82 commit 80534f8
Showing 1 changed file with 55 additions and 27 deletions.
82 changes: 55 additions & 27 deletions src/Monq.Core.Paging/Extensions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,29 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Monq.Core.Paging.Extensions
{
public static class QueryableExtensions
{
// if it needs checking search value - method TryParse else - null
static readonly Dictionary<Type, MethodInfo?> _supportedTypes = new()
{
[typeof(int)] = typeof(int).GetMethod(
nameof(int.TryParse),
new Type[] { typeof(string), typeof(int).MakeByRefType() })!,
[typeof(long)] = typeof(int).GetMethod(
nameof(int.TryParse),
new Type[] { typeof(string), typeof(int).MakeByRefType() })!,
[typeof(Guid)] = null,
};

static readonly MethodInfo _methodContains = typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) })!;
static readonly MethodInfo _methodToString = typeof(object).GetMethod(nameof(ToString))!;
static readonly MethodInfo _methodToLower = typeof(string).GetMethod(nameof(string.ToLower), Type.EmptyTypes)!;
static readonly MethodInfo _methodIsNullOrEmpty = typeof(string).GetMethod(nameof(string.IsNullOrEmpty), new[] { typeof(string) })!;

/// <summary>
/// Orderings the by.
/// </summary>
Expand Down Expand Up @@ -56,55 +74,65 @@ public static IQueryable<TSource> OrderByProperty<TSource>(this IQueryable<TSour

var props = typeof(TSource).GetPublicProperties(searching.Depth, searching.InSearch).ToList();
var stringProperties = props.Where(p => p.Property.PropertyType == typeof(string)).ToList();

if (!props.Any())
return null;

var parameter = Expression.Parameter(typeof(TSource), "t");
var methodContains = typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) });
var methodToLower = typeof(string).GetMethod(nameof(string.ToLower), Type.EmptyTypes);
var methodToString = typeof(object).GetMethod(nameof(ToString));
var methodIsNullOrEmpty = typeof(string).GetMethod(nameof(string.IsNullOrEmpty), new[] { typeof(string) });

var value = Expression.Constant(search.ToLower(), typeof(string));

var expList = new List<BinaryExpression>();

foreach (var (fullName, _) in stringProperties)
{
var expMember = parameter.GetPropertyExpressionUnSafe(fullName);
var extIsNullOrEmpty = Expression.Not(Expression.Call(methodIsNullOrEmpty, expMember.AddNullConditions()));
var extIsNullOrEmpty = Expression.Not(Expression.Call(_methodIsNullOrEmpty, expMember.AddNullConditions()));

Check warning on line 88 in src/Monq.Core.Paging/Extensions/QueryableExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and Publish Library

Possible null reference argument for parameter 'arg0' in 'MethodCallExpression Expression.Call(MethodInfo method, Expression arg0)'.

var expLower = Expression.Call(expMember, methodToLower);
var expContains = Expression.Call(expLower, methodContains, value);
var expLower = Expression.Call(expMember, _methodToLower);
var expContains = Expression.Call(expLower, _methodContains, value);

var extAnd = Expression.AndAlso(extIsNullOrEmpty, expContains);
expList.Add(extAnd);
}

if (long.TryParse(search, out _) || Guid.TryParse(search, out _))
{
var valueProperties = props.Where(p =>
p.Property.PropertyType == typeof(int)
|| p.Property.PropertyType == typeof(long)
|| p.Property.PropertyType == typeof(Guid));

var expression = Expression.Constant(true);
foreach (var (fullName, _) in valueProperties)
{
var expMember = parameter.GetPropertyExpression(fullName);
var ext = Expression.Call(expMember, methodToString);
var extContains = Expression.Call(ext, methodContains, value);
var extAnd = Expression.Equal(extContains, expression);
expList.Add(extAnd);
}
}
var valueExpressions = FormExpressionsForSupportedTypes(
props,
parameter,
search);

expList.AddRange(valueExpressions);

if (expList.Count == 0)
return null;

var body = expList.Aggregate(Expression.OrElse);
return Expression.Lambda<Func<TSource, bool>>(body, parameter);
}

private static IEnumerable<BinaryExpression> FormExpressionsForSupportedTypes(
IEnumerable<(string FullName, PropertyInfo Property)> targetProps,
ParameterExpression parameter,
string searchValue)
{
var result = new List<BinaryExpression>();
var valueExpr = Expression.Constant(searchValue.ToLower(), typeof(string));
var expression = Expression.Constant(true);

foreach (var (fullName, prop) in targetProps)
{
if (!_supportedTypes.TryGetValue(prop.PropertyType, out var tryParseMethod))
continue;

if (tryParseMethod != null
&& (bool)tryParseMethod.Invoke(null, new object?[] { searchValue, null })! == false)
continue;

var expMember = parameter.GetPropertyExpression(fullName);
var ext = Expression.Call(expMember, _methodToString);
var extContains = Expression.Call(ext, _methodContains, valueExpr);
var extAnd = Expression.Equal(extContains, expression);
result.Add(extAnd);
}
return result;
}
}
}

0 comments on commit 80534f8

Please sign in to comment.