ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【NetCore】调整表达式目录树实现并发布到NuGet

2021-10-18 02:01:01  阅读:204  来源: 互联网

标签:Name NetCore Age NuGet var new Expression public 表达式


调整表达式目录树实现并发布到NuGet

上一篇:【NetCore】使用表达式目录树实现动态组装Where的Linq表达式


代码仓库:https://gitee.com/wosperry/perry-expression-practice.git

如果想调试的话,可以安装git后,在控制台运行 git clone https://gitee.com/wosperry/perry-expression-practice.git 获取到代码,直接运行控制台程序即可(类库项目即是NuGet的那个代码)。


​ 上一篇使用表达式目录树实现根据参数类的配置,动态构建Lambda表达式。后面在老张的哲学弄的那个技术群里,群友给建议,让我把枚举值去掉,换成多个Attribute,仔细一想感觉是很有道理。我自己用的时候我也不希望写个特性还非要带一个要指定变量名的又臭又长的参数。

​ 于是就着手去改。花了两个小时,把这些改完了并第一次尝试发布到NuGet,然后把控制台程序修改为安装NuGet包,一次成功,蛮开心的。同时为了配置NuGet里的几个参数,顺便把Gitee项目设置为了Apache2.0开源了。


已经发布到NuGet,安装命令


# 程序包管理控制台
Install-Package Wosperry.ExpressionExtensions -Version 1.0.2

# .Net CLI
dotnet add package Wosperry.ExpressionExtensions --version 1.0.2

# PackageReference XML
<PackageReference Include="Wosperry.ExpressionExtensions" Version="1.0.2" />


使用参考

  • 实体类和参数类:

using Wosperry.ExpressionExtensions.Attributes;

namespace ConsoleApp
{
    public class Student
    {
        public string Name { get; set; }

        public string Code { get; set; }

        public int? Age { get; set; }
    }

    public class StudentListQueryParameter
    {
        [WhereLike]
        public string Name { get; set; }

        [WhereLike("Code")]
        public string Number { get; set; }

        [WhereEqual]
        public int? Age { get; set; }
    } 
}

  • 使用
// 需要引用: using Wosperry.ExpressionExtensions.Extends;

#region 假数据
var input = new StudentListQueryParameter()
{
    //Age = 18,
        Number = "B00",
    // Name = "张"
};

List<Student> students = new List<Student> {
    new Student{ Code="A001",Name="张三",Age=25},
    new Student{ Code="A002",Name="李四",Age=18},
    new Student{ Code="A003",Name="王五",Age=10},
    new Student{ Code="B001",Name="张四",Age=18},
    new Student{ Code="B002",Name="李五",Age=12},
    new Student{ Code="B003",Name="王六",Age=45},
    new Student{ Code="C001",Name="张五",Age=22},
    new Student{ Code="C001",Name="李六",Age=18},
    new Student{ Code="C001",Name="王七",Age=20},
};
#endregion

// 使用
var query = students.AsQueryable();
var data = query.Where(query.BuildLambda(input)).ToList();

// 如果是ABP的IRepository, 默认实现了IQueryable,所以调用如:
// var query = _repository.Where(_repository.BuildLambda(input));
// var data = await AsyncExecuter.ToListAsync(query);

展示一下调整后的代码,同样是希望能够帮助到未来的自己,还有暂时没有接触过这个东西的同行们。

事实上代码没有那么多,只是注释太密集了,没办法,我怕过两天我自己看不懂,多写了点注释,如果不想看那么多注释,可以拷贝出去删掉看。

public static class BinaryExpressionExtensions
{
    /// <summary>
    /// 连接一个Like(字符串的Contains)
    /// </summary>
    /// <param name="resultExpression">表达式</param>
    /// <param name="field">实体属性/字段</param>
    /// <param name="valueToCompare">对比的值</param>
    /// <param name="stringComparison">字符串对比规则</param>
    /// <returns> 返回示例: xxx && t.Contains(valueToCompare)</returns>
    public static BinaryExpression AndLikeLambda(this BinaryExpression resultExpression, MemberExpression field, ConstantExpression valueToCompare, StringComparison stringComparison = default)
    {
        // Contains 方法
        var containMethod = typeof(string).GetMethod(
            nameof(string.Contains),
            new Type[] { typeof(string), typeof(StringComparison) }
        );
        // 表达式:Contains 方法
        var containMethodExpression = Expression.Call(
            field,
            containMethod,
            valueToCompare,
            Expression.Constant(stringComparison)
        );
        return Expression.AndAlso(resultExpression, containMethodExpression);
    }

    /// <summary>
    /// 连接一个 Equals
    /// </summary>
    /// <param name="resultExpression"></param>
    /// <param name="field"></param>
    /// <param name="valueToCompare"></param>
    /// <returns></returns>
    public static BinaryExpression AndEqualLambda(this BinaryExpression resultExpression, MemberExpression field, ConstantExpression valueToCompare)
    {
        return Expression.AndAlso(
                resultExpression,
                Expression.Equal(
                    field,
                    Expression.Convert(valueToCompare, field.Type)
                )
            );
    }
}
public static class IQuaryableExtension
{
    /// <summary>
    /// 生成查询条件
    /// </summary>
    /// <typeparam name="TQueryParams">查询参数对象</typeparam>
    /// <param name="queryable">原有IQueryable</param>
    /// <exception cref="WhereAttribute.Field">
    /// 当 WhereAttribute.Field 指定的字段名在TEntity类型中不存在的时候,抛出异常
    /// </exception>
    /// <returns>lambda 表达式</returns>
    public static Expression<Func<TEntity, bool>> BuildLambda<TEntity, TQueryParams>(this IQueryable<TEntity> query, TQueryParams input, Action<BuildWhereOptions> options = null) where TEntity : class
    {
        // 配置项
        BuildWhereOptions buildWhereOptions = new BuildWhereOptions();
        if (!(options is null))
        {
            options(buildWhereOptions);
        }

        var t = Expression.Parameter(typeof(TEntity), "t");
        // 表达式:true
        var trueExpression = Expression.Constant(true);
        // 表达式:true && true
        var result = Expression.AndAlso(trueExpression, trueExpression);

        // 遍历入参所有属性 
        foreach (var prop in typeof(TQueryParams).GetProperties())
        {
            // 根据特性分别处理
            var attrs = prop.GetCustomAttributes();

            // 如果没有任何特性,则使用默认 DefaultAttribute 

            foreach (WhereAttribute attribute in attrs.Where(w => w is WhereAttribute))
            {
                // 表达式:实体属性 t
                //      当attr未指定 Field 值的时候,默认为与入参同名
                //      党attr指定了 Field 值的时候,使用 Field
                if (string.IsNullOrWhiteSpace(attribute.Field))
                {
                    attribute.Field = prop.Name;
                }

                // 对比的参数为空时,不做处理
                var valueToCompare = prop.GetValue(input);
                if (valueToCompare is null)
                    continue;

                // string 时,单独判断一次是否是空值
                if (prop.PropertyType == typeof(string) && string.IsNullOrWhiteSpace(valueToCompare as string))
                    continue;

                // 表达式实体属性:t.Name
                var fieldExpression = Expression.Property(t, attribute.Field);

                // 需要对比的值:value
                var valueExpression = Expression.Constant(valueToCompare, prop.PropertyType);

                #region 构建表达式

                // 相等
                // t.Name.Equals(value)
                if (attribute is WhereEqualAttribute)
                {
                    result = result.AndEqualLambda(fieldExpression, valueExpression);
                }

                // Like(字符串的Contains)
                // t.Name.Contains(value, builderWhereOptions.StringComparison)
                if (attribute is WhereLikeAttribute)
                {
                    result = result.AndLikeLambda(fieldExpression, valueExpression, buildWhereOptions.StringComparison);
                }

                // TODO:增加其他的对比类型(Larger、Less、In)

                #endregion
            }
        }
        // 如:t=>t.Age.Equals(input.Age) && t.Code.Contains(input.Number) && t.Name.Contains(input.Name)
        // 注意:input.Number是因为其有特性 [WhereLike("Code")]
        return Expression.Lambda<Func<TEntity, bool>>(result, t);
    }
}

标签:Name,NetCore,Age,NuGet,var,new,Expression,public,表达式
来源: https://www.cnblogs.com/wosperry/p/15418988.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有