Skip to content

Instantly share code, notes, and snippets.

@gnncl
Last active February 11, 2022 07:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gnncl/e424adefc3e08b607beee8d638759492 to your computer and use it in GitHub Desktop.
Save gnncl/e424adefc3e08b607beee8d638759492 to your computer and use it in GitHub Desktop.
Özellikle Postgresql EF Linq Contains aramalarında Case Insensitive ve Türkçe karakter problemleriyle uğraşmak istemiyorsanız kullanışlı olabilir.
using System;
using System.Collections.Generic;
using System.Linq;
namespace IQueryableSearchTest
{
public class Program
{
public static void Main(string[] args)
{
var studentList = new List<Student>
{
new(){ID = 1, Name = "Akın", Age = 22},
new(){ID = 2, Name = "Tülin", Age = 34},
new(){ID = 3, Name = "Çetin", Age = 31},
new(){ID = 4, Name = "Ayşin", Age = 18},
new(){ID = 5, Name = "Ayşin", Age = 42},
new(){ID = 6, Name = "Çetin", Age = 23}
};
// And ile arama yapmak için ayrı ayrı Search metodları eklenmelidir. Örneğin: Search("3", "ID", "Age").Search("tülin", "Name")
IQueryable<Student> MethodSyntax =
studentList.AsQueryable()
//.Where(x => x.Age == 31)
.Search("3", "ID", "Age") // '3' değerini hem ID hem de Age içinde aratmak için (OR olarak sorgular)
//.Search("in", "Name") // Ya da 'in' değerini Name içinde aratmak için
//.Search(23, op: Operator.Equals, "Age") Değeri Equals olarak aratmak için
.OrderBy(x => x.Age);
Console.WriteLine("Results =>");
//Iterate through the collection
foreach (var student in MethodSyntax)
{
Console.WriteLine($"ID: {student.ID}, Name: {student.Name}");
}
}
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
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; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment