:
public abstract class SearchQuery
{
public Type ResultType { get; set; }
public SearchQuery(Type searchResultType)
{
ResultType = searchResultType;
}
}
, , :
protected class Comparison : Attribute
{
public ExpressionType Type;
public Comparison(ExpressionType type)
{
Type = type;
}
}
protected class LinkedField : Attribute
{
public string TargetField;
public LinkedField(string target)
{
TargetField = target;
}
}
WHAT-, WHUTHER, . , "TxNumber" null, . , SearchField, : , , , .
private class SearchFilter<T>
{
public Expression<Func<object, bool>> ApplySearchCondition { get; set; }
public Expression<Func<T, bool>> SearchExpression { get; set; }
public object SearchValue { get; set; }
public IQueryable<T> Apply(IQueryable<T> query)
{
bool valid = ApplySearchCondition.Compile().Invoke(SearchValue);
return valid ? query.Where(SearchExpression) : query;
}
}
, , , "" ! !
- . ; int? , int.
private static Expression<Func<object, bool>> GetValidationExpression(Type type)
{
if (type != typeof(string) && !(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)))
throw new Exception("Non-nullable types not supported.");
if (type == typeof(string )) return t => !string.IsNullOrWhiteSpace((string)t);
if (type == typeof(int? )) return t => t != null && (int)t >= 0;
if (type == typeof(decimal? )) return t => t != null && (decimal)t >= decimal.Zero;
if (type == typeof(DateTime?)) return t => t != null && (DateTime?)t != DateTime.MinValue;
return t => t != null;
}
, , , .
"De-qualify" Field/Property names (, , , ). , "Order.Customer.Name" Orders, "Customer.Name", Order Order. , , , .:) , , , .
public static List<string> DeQualifyFieldName(string targetField, Type targetType)
{
var r = targetField.Split('.').ToList();
foreach (var p in targetType.Name.Split('.'))
if (r.First() == p) r.RemoveAt(0);
return r;
}
"" (, "" | "" ).
, .
private Expression<Func<T, bool>> GetSearchExpression<T>(
string targetField, ExpressionType comparison, object value)
{
var param = Expression.Parameter(ResultType, "t");
Expression left = null;
foreach (var part in DeQualifyFieldName(targetField, ResultType))
left = Expression.PropertyOrField(left == null ? param : left, part);
var right = Expression.Constant(value);
var binaryExpression = Expression.MakeBinary(comparison, left, right);
return Expression.Lambda<Func<T, bool>>(binaryExpression, param);
}
! , :
t => t.Customer.Name == "Searched Name"
t - ReturnType - , . t. /, , ( "", ). "" : , .
. , ! , , . .
; , , - :
protected IQueryable<T> ApplyFilters<T>(IQueryable<T> data)
{
if (data == null) return null;
IQueryable<T> retVal = data.AsQueryable();
var fields = GetType().GetFields().Cast<MemberInfo>()
.Concat(GetType().GetProperties())
.Where(f => f.GetCustomAttribute(typeof(LinkedField)) != null)
.Where(f => f.GetCustomAttribute(typeof(Comparison)) != null);
try
{
foreach (var f in fields)
{
var value = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).GetValue(this) : ((FieldInfo)f).GetValue(this);
if (value == null) continue;
Type t = f.MemberType == MemberTypes.Property ? ((PropertyInfo)f).PropertyType : ((FieldInfo)f).FieldType;
retVal = new SearchFilter<T>
{
SearchValue = value,
ApplySearchCondition = GetValidationExpression(t),
SearchExpression = GetSearchExpression<T>(GetTargetField(f), ((Comparison)f.GetCustomAttribute(typeof(Comparison))).Type, value)
}.Apply(retVal);
}
}
catch (Exception ex) { throw (ErrorInfo = ex); }
return retVal;
}
, / ( ), SearchFilter .
Clean-Up
, . , . , ?
, , :
private bool ValidateLinkedField(string fieldName)
{
Type currentType = ResultType;
foreach (string currentLevel in DeQualifyFieldName(fieldName, ResultType))
{
MemberInfo match = (MemberInfo)currentType.GetField(currentLevel) ?? currentType.GetProperty(currentLevel);
if (match == null) return false;
currentType = match.MemberType == MemberTypes.Property ? ((PropertyInfo)match).PropertyType
: ((FieldInfo)match).FieldType;
}
return true;
}
- . , , , , . VS 2015, , Program.cs Search.cs IDE .
, StackOverflow, , !