|
using System; |
|
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.Linq; |
|
using System.Linq.Expressions; |
|
using System.Reflection; |
|
|
|
namespace Shared.Base.Libraries.Helper |
|
{ |
|
public static class QueryExtension |
|
{ |
|
private static readonly MethodInfo TrimMethod = typeof(string).GetMethod("Trim", Type.EmptyTypes); |
|
private static readonly MethodInfo ContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); |
|
private static readonly MethodInfo ToStringMethod = typeof(object).GetMethod("ToString"); |
|
|
|
/// <summary> |
|
/// Sadece Or ile çalışır. And yapılacak property için ayrıca Where koşulu eklenmeli ardından bu metod çağrılmalı. |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
/// <param name="query"></param> |
|
/// <param name="propertyNames">Filtreleme yapılacak property adı.</param> |
|
/// <param name="value">Aranacak değer</param> |
|
/// <param name="op">Parametre olarak 'contains' ya da 'eq' alıyor.</param> |
|
/// <returns></returns> |
|
public static IQueryable<T> Search<T>(this IQueryable<T> query, object value, Operator op, params string[] propertyNames) |
|
{ |
|
var culture = CultureInfo.GetCultureInfo("tr-TR"); |
|
|
|
var filters = new List<Filter>(); |
|
|
|
foreach (var propertyName in propertyNames) |
|
{ |
|
var propertyType = GetPropertyType<T>(propertyName); |
|
|
|
filters.Add |
|
( |
|
new() |
|
{ |
|
Property = propertyName, |
|
Operator = op, |
|
Value = value is string val ? val.Trim() : value |
|
} |
|
); |
|
|
|
if (propertyType != typeof(string)) |
|
continue; |
|
|
|
filters.Add |
|
( |
|
new() |
|
{ |
|
Property = propertyName, |
|
Operator = op, |
|
Case = Case.ToLower, |
|
Value = value is string val1 ? val1.Trim().ToLower(culture) : value |
|
} |
|
); |
|
|
|
filters.Add |
|
( |
|
new() |
|
{ |
|
Property = propertyName, |
|
Operator = op, |
|
Case = Case.ToUpper, |
|
Value = value is string val2 ? val2.Trim().ToUpper(culture) : value |
|
} |
|
); |
|
} |
|
|
|
var expression = GetExpression<T>(filters); |
|
|
|
return query.Where(expression); |
|
} |
|
|
|
/// <summary> |
|
/// Sadece Or ile çalışır. And yapılacak property için ayrıca Where koşulu eklenmeli ardından bu metod çağrılmalı. |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
/// <param name="query"></param> |
|
/// <param name="value">Aranacak değer</param> |
|
/// <param name="propertyNames">Filtreleme yapılacak property adı. Params olarak eklenebilir.</param> |
|
/// <returns></returns> |
|
public static IQueryable<T> Search<T>(this IQueryable<T> query, object value, params string[] propertyNames) |
|
{ |
|
return query.Search(value, Operator.Contains, propertyNames); |
|
} |
|
|
|
private static Type GetPropertyType<T>(string property) |
|
{ |
|
var parameter = Expression.Parameter(typeof(T), "t"); |
|
|
|
var memberExpression = Expression.Property(parameter, property); |
|
|
|
return ((PropertyInfo)memberExpression.Member).PropertyType; |
|
} |
|
|
|
private static Expression<Func<T, bool>> GetExpression<T>(IEnumerable<Filter> filters) |
|
{ |
|
var parameter = Expression.Parameter(typeof(T), "t"); |
|
|
|
Expression orExpression = null; |
|
|
|
foreach (var filter in filters) |
|
{ |
|
orExpression = |
|
orExpression == null |
|
? GetExpression(parameter, filter, filter.Case) |
|
: Expression.Or(orExpression, GetExpression(parameter, filter, filter.Case)); |
|
} |
|
|
|
return orExpression == null |
|
? default |
|
: Expression.Lambda<Func<T, bool>>(orExpression, parameter); |
|
} |
|
|
|
private static Expression GetExpression(ParameterExpression parameter, Filter filter, Case charCase) |
|
{ |
|
if (filter.Value == null) |
|
return Expression.Equal(Expression.Default(typeof(int)), Expression.Default(typeof(int))); |
|
|
|
var caseMethodInfo = typeof(string).GetMethod(charCase.ToString(), Type.EmptyTypes); |
|
|
|
var memberExpression = Expression.Property(parameter, filter.Property); |
|
|
|
UnaryExpression constant; |
|
|
|
if (filter.Value is string val) |
|
constant = Expression.Convert(Expression.Constant(val), typeof(string)); |
|
|
|
else |
|
constant = Expression.Convert(Expression.Constant(filter.Value), memberExpression.Type); |
|
|
|
switch (filter.Operator) |
|
{ |
|
// Test edilmedi |
|
case Operator.Equals: |
|
if (memberExpression.Type == typeof(string)) |
|
{ |
|
var trimCallExpression = Expression.Call(memberExpression, TrimMethod); |
|
|
|
var methodCallExpression = |
|
filter.Case != Case.Default |
|
? Expression.Call(trimCallExpression, caseMethodInfo) |
|
: trimCallExpression; |
|
|
|
return Expression.Equal(methodCallExpression, constant); |
|
} |
|
else |
|
return Expression.Equal(memberExpression, constant); |
|
|
|
case Operator.Contains: |
|
if (memberExpression.Type == typeof(string)) |
|
{ |
|
var trimCallExpression = Expression.Call(memberExpression, TrimMethod); |
|
|
|
var methodCallExpression = |
|
filter.Case != Case.Default |
|
? Expression.Call(trimCallExpression, caseMethodInfo) |
|
: trimCallExpression; |
|
|
|
return Expression.Call(methodCallExpression, ContainsMethod, constant); |
|
} |
|
|
|
if (memberExpression.Type == typeof(int)) |
|
{ |
|
var toStringCallExpression = Expression.Call(memberExpression, ToStringMethod); |
|
|
|
var trimCallExpression = Expression.Call(toStringCallExpression, TrimMethod); |
|
|
|
return Expression.Call(trimCallExpression, ContainsMethod, constant); |
|
} |
|
|
|
else |
|
return Expression.Call(memberExpression, ContainsMethod, constant); |
|
} |
|
|
|
return Expression.Equal(Expression.Default(typeof(int)), Expression.Default(typeof(int))); |
|
} |
|
} |
|
|
|
public enum Case |
|
{ |
|
Default, |
|
ToUpper, |
|
ToLower |
|
} |
|
|
|
public enum Operator |
|
{ |
|
Equals, |
|
Contains |
|
} |
|
|
|
public class Filter |
|
{ |
|
public string Property { get; init; } |
|
public Operator Operator { get; init; } |
|
public Case Case { get; init; } |
|
public object Value { get; init; } |
|
} |
|
} |