zoukankan      html  css  js  c++  java
  • 利用 DynamicLinq 实现简单的动态表达式构建查询

    平时使用 LINQ 进行一些简单的条件拼接查询一般都会这样操作:

    public class SearchInputDto
    {
        public string ConditionA { get; set; }
        public int? ConditionB { get; set; }
        public string ConditionC { get; set; }
    }
    

    这里有三个条件,是前端传入的搜索条件,然后我们来编写一个查询语句:

    public Task Search(SearchInputDto input)
    {
        var queryResult = _db.Where(z=>(input.ConditionA == null || z.Name == input.ConditionA) 
                                    && (input.ConditionB == null || z.Number == input.ConditionB)
                                    && (input.ConditionC == null || z.Address == input.ConditionC));
        
        // 执行其他操作...
        
        return Task.FromResult(0);
    }
    

    因为我们前端传入的条件不是固定的,所以有可能会出现有的条件没有传入的情况,如果是 SQL 的动态拼接 SQL 就可以了,而 Linq 你肯定是没法动态拼接的,只有自己构建一个表达式树传入到 IQuerable<T>.Where(Expression<Func<T,bool>> expression) 里面进行查询。

    纯手工构建表达式树也不是不可以,只是略微麻烦,而我们则可以借助 System.Linq.Dynamic.Core 来方便的实现动态查询语句拼接。

    他的常规用法如下:

    官方 WIKI 地址:https://github.com/StefH/System.Linq.Dynamic.Core/wiki/Dynamic-Expressions

    var query = db.Customers
                  .Where("City == @0 and Orders.Count >= @1", "London", 10)
                  .OrderBy("CompanyName")
                  .Select("new(CompanyName as Name, Phone)");
    

    既然是字符串那么就可以拼接,我们来做一下改造。

    首先去 NuGet 当中搜索 System.Linq.Dynamic.Core 库,安装之后我们来重新编写之前的查询范例,首先我们来写一个构建器,用于构建我们的表达式树:

    using Abp.Runtime.Caching;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Dynamic.Core;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    
    namespace Abp.Linq.Expressions
    {
        public class ExpressionBuilder<TEntity, TSearchDto>
        {
            // 其实这里也可以通过传入 params Expression<Func<TRelateEntity, object>>[] selectFields 来构建
            public Expression<Func<TEntity, bool>> Build(string[] excludeFields, TSearchDto dto)
            {
                var parameters = GenerateParametersDictionary(excludeFields, dto);
    
                StringBuilder sb = new StringBuilder();
                var fieldNames = parameters.Keys.ToList();
    
                // 动态拼接
                for (int i = 0; i < fieldNames.Count; i++)
                {
                    sb.Append(fieldNames[i]).Append($" == @{i}").Append(" && ");
                }
    
                var lambdaStr = sb.ToString();
                lambdaStr = lambdaStr.Substring(0, lambdaStr.Length - " && ".Length);
    
                // 构建表达式
                return DynamicExpressionParser.ParseLambda<TEntity, bool>(new ParsingConfig(), false, lambdaStr, parameters.Values.ToArray());
            }
    
            // 构建参数/值键值对,如果参数值为 NULL 则不进行构建
            private Dictionary<string, object> GenerateParametersDictionary(string[] excludeFields, TSearchDto dto)
            {
                var typeInfo = typeof(TSearchDto);
                var properties = typeInfo.GetProperties();
                var parameters = new Dictionary<string, object>();
    
                foreach (var property in properties)
                {
                    var propertyValue = property.GetValue(dto);
    
                    if (propertyValue == null) continue;
                    if (excludeFields == null) continue;
                    if (excludeFields.Contains(property.Name)) continue;
                    if (parameters.ContainsKey(property.Name)) continue;
    
                    parameters.Add(property.Name, propertyValue);
                }
    
                return parameters;
            }
        }
    }
    

    用法很简单,用刚才的代码作为一个例子:

    public Task Search(SearchInputDto input)
    {
        var builder = new ExpressionBuilder<EntityA,SearchInputDto>();
        var queryResult = _db.Where(builder.Build(null,input));
        
        // 执行其他操作...
        
        return Task.FromResult(0);
    }
    

    可以看到已经变得十分简洁,这里仅仅作为抛砖引玉,其实还有更多高级的用法,这里不再赘述。

  • 相关阅读:
    “TensorFlow 开发者出道计划”全攻略,玩转社区看这里!
    魔改合成大西瓜
    自定义注解!绝对是程序员装逼的利器!!
    Python中的join()函数的用法
    Python中的split()函数的用法
    linux 完全卸载mysql数据库
    域名被盗后还能不能找回
    在选择域名后缀时应该考虑到的问题
    什么叫域名劫持 和域名解析有什么区别
    河北重大技术需求系统04
  • 原文地址:https://www.cnblogs.com/myzony/p/9143692.html
Copyright © 2011-2022 走看看