You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化这段EF Core LINQ代码,实现DRY原则减少重复?

解决方案

要消除重复逻辑并构建可复用的Replace调用链,可以通过客户端字符串清理方法和EF Core表达式树构建方法实现,既保证代码复用,又能让EF Core正确解析生成对应SQL。

1. 提取客户端字符串清理逻辑

先实现一个可复用的方法,用来清理输入的查询字符串,接收要移除的字符/字符串数组,循环执行替换:

public static class StringExtensions
{
    public static string Clean(this string input, params string[] removeStrings)
    {
        if (string.IsNullOrWhiteSpace(input))
            return string.Empty;
        
        var cleaned = input;
        foreach (var str in removeStrings)
        {
            cleaned = cleaned.Replace(str, string.Empty);
        }
        return cleaned;
    }
}

2. 构建EF Core可解析的Replace调用链

EF Core需要将表达式树转换成SQL,因此不能直接用普通方法,得通过表达式树动态构建Collate+多轮Replace的调用链。实现通用方法处理实体属性表达式:

using System.Linq.Expressions;

public static class QueryableExtensions
{
    // 定义默认要移除的字符串集合,可按需调整
    private static readonly string[] _defaultRemoveStrings = { "-", " ", "’", "'" };

    // 生成清理后的列表达式:Collate → 链式Replace
    public static Expression<Func<T, string>> CleanColumn<T>(Expression<Func<T, string>> propertyExpr)
    {
        return CleanColumn(propertyExpr, _defaultRemoveStrings);
    }

    public static Expression<Func<T, string>> CleanColumn<T>(Expression<Func<T, string>> propertyExpr, params string[] removeStrings)
    {
        // 构建Collate调用:EF.Functions.Collate(属性, "binary_ai")
        var collateMethod = typeof(EF.Functions).GetMethod(nameof(EF.Functions.Collate), new[] { typeof(string), typeof(string) })!;
        var collateExpr = Expression.Call(collateMethod, propertyExpr.Body, Expression.Constant("binary_ai"));

        // 链式拼接Replace调用
        Expression currentExpr = collateExpr;
        foreach (var str in removeStrings)
        {
            var replaceMethod = typeof(string).GetMethod(nameof(string.Replace), new[] { typeof(string), typeof(string) })!;
            currentExpr = Expression.Call(currentExpr, replaceMethod, Expression.Constant(str), Expression.Constant(string.Empty));
        }

        return Expression.Lambda<Func<T, string>>(currentExpr, propertyExpr.Parameters);
    }

    // 封装Where条件:清理列 + 包含查询
    public static IQueryable<T> WhereCleanContains<T>(this IQueryable<T> query, Expression<Func<T, string>> propertyExpr, string input, params string[] removeStrings)
    {
        if (string.IsNullOrWhiteSpace(input))
            return query;
        
        var cleanedInput = input.Clean(removeStrings);
        var cleanedColumnExpr = CleanColumn(propertyExpr, removeStrings);
        
        // 构建Contains表达式
        var containsMethod = typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) })!;
        var containsExpr = Expression.Call(cleanedColumnExpr.Body, containsMethod, Expression.Constant(cleanedInput));
        var whereExpr = Expression.Lambda<Func<T, bool>>(containsExpr, cleanedColumnExpr.Parameters);
        
        return query.Where(whereExpr);
    }
}

3. 重构原有代码

用上面的方法简化查询逻辑,彻底消除重复:

var request = GetRequestValuesFromApi();
var query = _context.Persons.AsQueryable();
var removeStrings = new[] { "-", " ", "’", "'" };

// 直接调用封装好的扩展方法
query = query.WhereCleanContains(x => x.Firstname!, request.Firstname, removeStrings);
query = query.WhereCleanContains(x => x.Lastname!, request.Lastname, removeStrings);

// ...

var results = query.ToList();

关键说明

  • 客户端Clean方法负责内存中处理输入字符串,和EF Core无关联。
  • EF Core部分通过表达式树动态构建调用链,确保EF能将其解析为SQL中的REPLACE函数,不会出现无法翻译的错误。
  • 要移除的字符串集中定义,后续修改只需调整数组内容,避免多处同步修改。

内容的提问来源于stack exchange,提问作者Philippe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:05:27