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);
    }
    

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

  • 相关阅读:
    70.BOM
    69.捕获错误try catch
    68.键盘事件
    523. Continuous Subarray Sum
    901. Online Stock Span
    547. Friend Circles
    162. Find Peak Element
    1008. Construct Binary Search Tree from Preorder Traversal
    889. Construct Binary Tree from Preorder and Postorder Traversal
    106. Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/myzony/p/9143692.html
Copyright © 2011-2022 走看看