如何优化这段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
相关产品推荐
相关产品推荐

