首页 > 其他分享 >还在拼冗长的WhereIf吗?100行代码解放这个操作

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

时间:2024-06-05 17:11:00浏览次数:22  
标签:CompareType WhereIf CompareSite source str 冗长 100 public

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

普通做法

最原始的做法我们是先通过If()判断是否需要进行数据过滤,然后再对数据源使用Where来过滤数据。
示例如下:

if(!string.IsNullOrWhiteSpace(str))
{
    query = query.Where(a => a == str);
}

封装WhereIf做法

进阶一些的就把普通做法的代码封装成一个扩展方法,WhereIf指代一个名称,也可以有其他名称,本质是一样的。
示例如下:

public static IQueryable<T> WhereIf<T>([NotNull] this IQueryable<T> query, bool condition, Expression<Func<T, int, bool>> predicate)
{
    return condition
        ? query.Where(predicate)
        : query;
}

使用方式:

query.WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);

封装WhereIf做法相比普通做法,已经可以减少我们代码的很多If块了,看起来也优雅一些。
但是如果查询条件增多的话,我们依旧需要写很多WhereIf,就会有这种现象:

query
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str)
      .WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);

条件一但增多很多的话,这样一来代码看起来就又不够优雅了~

这时候就想,如果只用一个Where传进去一个对象,自动解析条件进行数据过滤,是不是就很棒呢~

WhereObj做法

想法来了,那就动手实现一下。

首先我们需要考虑如何对对象的属性进行标记来获取我们作为条件过滤的对应属性。那就得加一个Attribute,这里实现一个CompareAttribute,用于对对象的属性进行标记。

[AttributeUsage(AttributeTargets.Property)]
public class CompareAttribute : Attribute
{
    public CompareAttribute(CompareType compareType)
    {
        CompareType = compareType;
    }

    public CompareAttribute(CompareType compareType, string compareProperty) : this(compareType)
    {
        CompareProperty = compareProperty;
    }

    public CompareType CompareType { get; set; }

    public CompareSite CompareSite { get; set; } = CompareSite.LEFT;

    public string? CompareProperty { get; set; }
}

public enum CompareType
{
    Equal,
    NotEqual,
    GreaterThan,
    GreaterThanOrEqual,
    LessThan,
    LessThanOrEqual,
    Contains,
    StartsWith,
    EndsWith,
    IsNull,
    IsNotNull
}

public enum CompareSite
{
    RIGHT,
    LEFT
}

这里CompareType表示要进行比较的操作,很简单,一目了然。
CompareSite则表示在进行比较的时候比较的数据处于比较符左边还是右边,在CompareAttribute给与默认值在左边,表示比较的源数据处于左边。
CompareProperty则表示比较的属性名称,空的话则直接使用对象名称,如果有值则优先使用。

Attribute搞定了,接下来则实现我们的WhereObj
这里由于需要动态的拼接表达式,这里使用了DynamicExpresso.Core库来进行动态表达式生成。
先上代码:

namespace System.Linq;

public static class WhereExtensions
{
    public static IQueryable<T> WhereObj<T>(this IQueryable<T> 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<CompareAttribute>();

            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<Func<T, bool>>(whereExpression.ToString(), "q"));
        }
        return queryable;
    }
    public static IEnumerable<T> WhereObj<T>(this IEnumerable<T> 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<CompareAttribute>();

            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<Func<T, bool>>(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} <= {target}" : $"{target} >= {source}",
            CompareType.LessThan => compareSite == CompareSite.LEFT ? $"{source} > {target}" : $"{target} < {source}",
            CompareType.LessThanOrEqual => compareSite == CompareSite.LEFT ? $"{source} >= {target}" : $"{target} <= {source}",
            CompareType.Contains => compareSite == CompareSite.LEFT ? $"{source}.Contains({target})" : $"{target}.Contains({source})",
            CompareType.StartsWith => compareSite == CompareSite.LEFT ? $"{source}.StartsWith({target})" : $"{target}.StartsWith({source})",
            CompareType.EndsWith => compareSite == CompareSite.LEFT ? $"{source}.EndsWith({target})" : $"{target}.EndsWith({source})",
            CompareType.IsNull => $"{source} == null",
            CompareType.IsNotNull => $"{source} != null",
            _ => throw new NotSupportedException()
        };
    }
}

代码对IEnumerable和IQueryable都进行了扩展,总共行数100行。
在WhereObj中,我们传入一个parameterObject,然后获取对象的所有加了CompareAttribute的属性。
然后进行循环拼接条件。在循环中我们先判断属性是否有值,有值才会添加表达式。所以建议条件属性都为可空类型。

if(property.GetValue(parameterObject) == null)
{
    continue;
}

然后获取属性的CompareAttribute, 先指定条件属性名称,在判断属性是否在源对象存在,如果不存在则不处理。

if (typeof(T).GetProperty(propertyName) == null)
{
    continue;
}

最后就是根据CompareType来动态生成拼接的表达式了。
BuildCompareExpression方法根据CompareType和CompareSite动态拼接表达式字符串,然后使用Interpreter.ParseAsExpression<Func<T, bool>>转换成我们的表达式类型。就完成啦。

测试效果

搞一个Customer类和CustomerFilter,再搞一个数据。

namespace Test
{
    public class Customer
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public char Gender { get; set; }
    }
    public class CustomerFilter
    {
        [Compare(CompareType.StartsWith)]
        public string? Name { get; set; }
        [Compare(CompareType.Contains, "Name", CompareSite = CompareSite.RIGHT)]
        public List<string>? Names { get; set; }
        [Compare(CompareType.GreaterThan)]
        public int? Age { get; set; }
        [Compare(CompareType.Equal)]
        public char? Gender { get; set; }
    }

    public class T
    {
        public static IEnumerable<Customer> customers = (new List<Customer> {
            new Customer() { Name = "David", Age = 31, Gender = 'M' },
            new Customer() { Name = "Mary", Age = 29, Gender = 'F' },
            new Customer() { Name = "Jack", Age = 2, Gender = 'M' },
            new Customer() { Name = "Marta", Age = 1, Gender = 'F' },
            new Customer() { Name = "Moses", Age = 120, Gender = 'M' },
            }).AsEnumerable();
    }

}

测试代码

T.customers.WhereObj(new CustomerFilter() 
{
    //Name = "M",
    Names = ["Mary", "Jack"],
    //Age = 20,
    //Gender = 'M'
})
    .ToList().ForEach(c => Console.WriteLine(c.Name));


可以看到正常执行。
这样我们在应对条件很多的数据过滤的时候,就可以只用一个WhereObj就可以代替很多个WhereIf的拼接了。同时,在添加新条件的时候我们也无需修改其他业务代码。

标签:CompareType,WhereIf,CompareSite,source,str,冗长,100,public
From: https://www.cnblogs.com/fanshaoO/p/18233291

相关文章

  • Python从0到100(二十九):requests模块处理cookie
    1爬虫中使用cookie为了能够通过爬虫获取到登录后的页面,或者是解决通过cookie的反扒,需要使用request来处理cookie相关的请求1.1爬虫中使用cookie的利弊带上cookie的好处能够访问登录后的页面能够实现部分反反爬带上cookie的坏处一套cookie往往对应的是一个用户......
  • 【华为OD】D卷真题100分:高矮个子排队 JavaScript代码实现[思路+代码]
    【华为OD】2024年C、D卷真题集:最新的真题集题库C/C++/Java/python/JavaScript【华为OD】2024年C、D卷真题集:最新的真题集题库C/C++/Java/python/JavaScript-CSDN博客JS、C、python、Java、C++代码实现:【华为OD】D卷真题100分:高矮个子排队JavaScript代码实现[思路+代码]-C......
  • 100. Same Tree
    Giventherootsoftwobinarytreespandq,writeafunctiontocheckiftheyarethesameornot.Twobinarytreesareconsideredthesameiftheyarestructurallyidentical,andthenodeshavethesamevalue.Example1:Input:p=[1,2,3],q=[1,2......
  • 一键导入excel电子表格到mysql,一次导入100个100万行的电子表,合并导入到一张数据库表里
       适用场景:从数据库中同一张大数据表导出的N个excel表格,他们的结构是相同的。那你说我不需要这个功能,我分表导入数据库,然后我写sql语句合并在一起就完事了。我以前也一直是这样处理的。几个表或者十多个表合并成一个表,我们写sql,忍忍也就过去了。但是上百个表合并成一个表......
  • C#开源实用的工具类库,集成超过1000多种扩展方法
    前言今天大姚给大家分享一个C#开源(MITLicense)、免费、实用且强大的工具类库,集成超过1000多种扩展方法增强.NETFramework和.NETCore的使用效率:Z.ExtensionMethods。直接项目引入类库使用在你的对应项目中NuGet包管理器中搜索:Z.ExtensionMethods安装即可使用。支持.NETS......
  • 瑞云渲染动画:新用户专属100元渲染券领取指南
    瑞云渲染作为业界知名的云渲染服务提供商,始终为用户提高高效、稳定的渲染解决方案。为庆祝“动画”新用户的加入,特上线新人专属福利——100渲染通用劵。这不仅是对新用户的热情,更是对提升创作效率的承诺。下面一起来看看如何轻松领取这份专属好礼把。活动时间:2024.5.31起,长期有......
  • 2023-2025年最值得选择的Java毕业设计选题大全:1000个热门选题推荐✅✅✅
    ......
  • 函数递归输出1~100的数字及递归的栈溢出问题
    什么是递归?递归就是函数⾃⼰调⽤⾃⼰递归中的递就是递推的意思,归就是回归的意思如果递归就像循环一样,打一个大的复杂问题转化一个小的问题,但是要与原问题相似,分解成规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了,所以递归的思考⽅式就是把⼤事化⼩的过程递归......
  • Python从0到100(三十):requests模块的其他方法
    1requests中cookirJar的处理方法使用request获取的resposne对象,具有cookies属性,能够获取对方服务器设置在本地的cookie,但是如何使用这些cookie呢?1.1方法介绍response.cookies是CookieJar类型使用requests.utils.dict_from_cookiejar,能够实现把cookiejar对象转化为字典......
  • BUUCTF-Misc(91-100)
    [MRCTF2020]CyberPunk运行一下,他说2020.9.17才开始然后改一下系统时间就拿到flagflag{We1cOm3_70_cyber_security}[安洵杯2019]Attack参考:[BUUMISC刷题记录安洵杯2019]Attack-云千-博客园(cnblogs.com)找到了一个formost分离一下,在压缩包找到了然后在导出......