翼度科技»论坛 编程开发 .net 查看内容

还在拼冗长的WhereIf吗?100行代码解放这个操作

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
通常我们在做一些数据过滤的操作的时候,经常需要做一些判断再进行是否要对其进行条件过滤。
普通做法

最原始的做法我们是先通过If()判断是否需要进行数据过滤,然后再对数据源使用Where来过滤数据。
示例如下:
  1. if(!string.IsNullOrWhiteSpace(str))
  2. {
  3.     query = query.Where(a => a == str);
  4. }
复制代码
封装WhereIf做法

进阶一些的就把普通做法的代码封装成一个扩展方法,WhereIf指代一个名称,也可以有其他名称,本质是一样的。
示例如下:
  1. public static IQueryable<T> WhereIf<T>([NotNull] this IQueryable<T> query, bool condition, Expression<Func<T, int, bool>> predicate)
  2. {
  3.     return condition
  4.         ? query.Where(predicate)
  5.         : query;
  6. }
复制代码
使用方式:
  1. query.WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);
复制代码
封装WhereIf做法相比普通做法,已经可以减少我们代码的很多If块了,看起来也优雅一些。
但是如果查询条件增多的话,我们依旧需要写很多WhereIf,就会有这种现象:
  1. query
  2.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  3.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  4.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  5.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  6.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  7.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  8.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  9.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  10.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
  11.       .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);
复制代码
条件一但增多很多的话,这样一来代码看起来就又不够优雅了~
这时候就想,如果只用一个Where传进去一个对象,自动解析条件进行数据过滤,是不是就很棒呢~
WhereObj做法

想法来了,那就动手实现一下。
首先我们需要考虑如何对对象的属性进行标记来获取我们作为条件过滤的对应属性。那就得加一个Attribute,这里实现一个CompareAttribute,用于对对象的属性进行标记。
  1. [AttributeUsage(AttributeTargets.Property)]
  2. public class CompareAttribute : Attribute
  3. {
  4.     public CompareAttribute(CompareType compareType)
  5.     {
  6.         CompareType = compareType;
  7.     }
  8.     public CompareAttribute(CompareType compareType, string compareProperty) : this(compareType)
  9.     {
  10.         CompareProperty = compareProperty;
  11.     }
  12.     public CompareType CompareType { get; set; }
  13.     public CompareSite CompareSite { get; set; } = CompareSite.LEFT;
  14.     public string? CompareProperty { get; set; }
  15. }
  16. public enum CompareType
  17. {
  18.     Equal,
  19.     NotEqual,
  20.     GreaterThan,
  21.     GreaterThanOrEqual,
  22.     LessThan,
  23.     LessThanOrEqual,
  24.     Contains,
  25.     StartsWith,
  26.     EndsWith,
  27.     IsNull,
  28.     IsNotNull
  29. }
  30. public enum CompareSite
  31. {
  32.     RIGHT,
  33.     LEFT
  34. }
复制代码
这里CompareType表示要进行比较的操作,很简单,一目了然。
CompareSite则表示在进行比较的时候比较的数据处于比较符左边还是右边,在CompareAttribute给与默认值在左边,表示比较的源数据处于左边。比如Contains操作,有时候是判断源字符串是否包含子字符串,此时应该是sourceStr.Contains(str),有时候是判断源字符串是否在某个集合字符串中则是ListString.Contains(sourceStr)。
CompareProperty则表示比较的属性名称,空的话则直接使用对象名称,如果有值则优先使用。
Attribute搞定了,接下来则实现我们的WhereObj
这里由于需要动态的拼接表达式,这里使用了DynamicExpresso.Core库来进行动态表达式生成。
先上代码:
[code]namespace System.Linq;public static class WhereExtensions{    public static IQueryable WhereObj(this IQueryable queryable, object parameterObject)    {        var interpreter = new Interpreter();        interpreter = interpreter.SetVariable("o", parameterObject);        var properties = parameterObject.GetType().GetProperties().Where(p => p.CustomAttributes.Any(a=>a.AttributeType == typeof(CompareAttribute)));        var whereExpression = new StringBuilder();        foreach (var property in properties)        {            if(property.GetValue(parameterObject) == null)            {                continue;            }            var compareAttribute = property.GetCustomAttribute();            var propertyName = compareAttribute!.CompareProperty ?? property.Name;            if (typeof(T).GetProperty(propertyName) == null)            {                continue;            }            if (whereExpression.Length > 0)            {                whereExpression.Append(" && ");            }            whereExpression.Append(BuildCompareExpression(propertyName, property, compareAttribute.CompareType, compareAttribute.CompareSite));        }        if(whereExpression.Length > 0)        {            return queryable.Where(interpreter.ParseAsExpression(whereExpression.ToString(), "q"));        }        return queryable;    }    public static IEnumerable WhereObj(this IEnumerable enumerable, object parameterObject)    {        var interpreter = new Interpreter();        interpreter = interpreter.SetVariable("o", parameterObject);        var properties = parameterObject.GetType().GetProperties().Where(p => p.CustomAttributes.Any(a=>a.AttributeType == typeof(CompareAttribute)));        var whereExpression = new StringBuilder();        foreach (var property in properties)        {            if(property.GetValue(parameterObject) == null)            {                continue;            }            var compareAttribute = property.GetCustomAttribute();            var propertyName = compareAttribute!.CompareProperty ?? property.Name;            if (typeof(T).GetProperty(propertyName) == null)            {                continue;            }            if (whereExpression.Length > 0)            {                whereExpression.Append(" && ");            }            whereExpression.Append(BuildCompareExpression(propertyName, property, compareAttribute.CompareType, compareAttribute.CompareSite));        }        if(whereExpression.Length > 0)        {            return enumerable.Where(interpreter.ParseAsExpression(whereExpression.ToString(), "q").Compile());        }        return enumerable;    }    private static string BuildCompareExpression(string propertyName, PropertyInfo propertyInfo, CompareType compareType, CompareSite compareSite)    {        var source = $"q.{propertyName}";        var target = $"o.{propertyInfo.Name}";        return compareType switch        {            CompareType.Equal => compareSite == CompareSite.LEFT ? $"{source} == {target}" : $"{target} == {source}",            CompareType.NotEqual => compareSite == CompareSite.LEFT ? $"{source} != {target}" : $"{target} != {source}",            CompareType.GreaterThan => compareSite == CompareSite.LEFT ? $"{source} < {target}" : $"{target} > {source}",            CompareType.GreaterThanOrEqual => compareSite == CompareSite.LEFT ? $"{source} = {source}",            CompareType.LessThan => compareSite == CompareSite.LEFT ? $"{source} > {target}" : $"{target} < {source}",            CompareType.LessThanOrEqual => compareSite == CompareSite.LEFT ? $"{source} >= {target}" : $"{target}

举报 回复 使用道具