LINQ Select中匿名函数优化:避免重复Select分支实现
简化LINQ重复分支的实现方案
一、问题根源
你遇到的错误是因为直接将lambda表达式赋值给decimal类型的Price字段——lambda是委托或表达式对象,并非直接的decimal计算结果,必须先执行逻辑得到值再赋值。
二、内存集合(LINQ to Objects)解决方案
把价格计算逻辑抽象为Func<IEnumerable<数据类型>, decimal>委托,复用同一套Select逻辑即可:
// 假设原始数据类型为Product,目标类型为Item Func<IEnumerable<Product>, decimal> priceCalculator; // 根据条件选择计算逻辑 if (当前条件为maxPrice) { priceCalculator = products => products.Max(p => p.Price); } else if (当前条件为minPrice) { priceCalculator = products => products.Min(p => p.Price); } else if (当前条件为averagePrice) { priceCalculator = products => products.Average(p => (decimal)p.Price); } else { priceCalculator = _ => 0; // 默认兜底逻辑 } // 复用Select语句,仅传入预先定义的计算逻辑 var items = productGroups.Select(group => new Item { Category = group.Key, Price = priceCalculator(group) }).ToList();
三、EF等ORM(LINQ to Entities)解决方案
如果是EF这类需要将表达式转换为SQL的场景,不能用普通委托,需使用Expression<Func<IGrouping<分组键类型, 数据类型>, decimal>>表达式树:
方式1:手动构建表达式树
Expression<Func<IGrouping<string, Product>, decimal>> priceExpr; // 根据条件选择表达式 if (当前条件为maxPrice) { priceExpr = group => group.Max(p => p.Price); } else if (当前条件为minPrice) { priceExpr = group => group.Min(p => p.Price); } else if (当前条件为averagePrice) { priceExpr = group => group.Average(p => (decimal)p.Price); } else { priceExpr = _ => 0; } // 构建通用的Select表达式 var param = Expression.Parameter(typeof(IGrouping<string, Product>), "group"); var categoryProp = Expression.Property(param, nameof(IGrouping<string, Product>.Key)); var priceCalculation = Expression.Invoke(priceExpr, param); var itemCtor = typeof(Item).GetConstructor(new[] { typeof(string), typeof(decimal) }); var selectBody = Expression.New(itemCtor, categoryProp, priceCalculation); var selectExpr = Expression.Lambda<Func<IGrouping<string, Product>, Item>>(selectBody, param); // 执行查询 var items = dbContext.Products .GroupBy(p => p.Category) .Select(selectExpr) .ToList();
方式2:封装表达式组合工具
// 封装通用的表达式组合方法 public static Expression<Func<TGroup, TResult>> CombineSelect<TGroup, TResult>( Expression<Func<TGroup, string>> categorySelector, Expression<Func<TGroup, decimal>> priceSelector) { var param = categorySelector.Parameters[0]; var categoryExpr = categorySelector.Body; var priceExpr = priceSelector.Body; var itemCtor = typeof(TResult).GetConstructor(new[] { typeof(string), typeof(decimal) }); return Expression.Lambda<Func<TGroup, TResult>>( Expression.New(itemCtor, categoryExpr, priceExpr), param); } // 使用时 Expression<Func<IGrouping<string, Product>, decimal>> priceSelector; // 按条件赋值priceSelector... var selectExpr = CombineSelect<IGrouping<string, Product>, Item>( group => group.Key, priceSelector); var items = dbContext.Products.GroupBy(p => p.Category).Select(selectExpr).ToList();
四、进阶优化:策略模式消除if-else
如果条件较多,可将每个计算逻辑封装为策略类,通过字典或工厂获取,彻底消除分支:
// 定义策略接口 public interface IPriceCalculationStrategy { // 内存集合场景的计算方法 decimal Calculate(IEnumerable<Product> products); // ORM场景的表达式方法 Expression<Func<IGrouping<string, Product>, decimal>> GetExpression(); } // 实现Max价格策略 public class MaxPriceStrategy : IPriceCalculationStrategy { public decimal Calculate(IEnumerable<Product> products) => products.Max(p => p.Price); public Expression<Func<IGrouping<string, Product>, decimal>> GetExpression() => group => group.Max(p => p.Price); } // 实现Min价格策略 public class MinPriceStrategy : IPriceCalculationStrategy { public decimal Calculate(IEnumerable<Product> products) => products.Min(p => p.Price); public Expression<Func<IGrouping<string, Product>, decimal>> GetExpression() => group => group.Min(p => p.Price); } // 实现Average价格策略 public class AveragePriceStrategy : IPriceCalculationStrategy { public decimal Calculate(IEnumerable<Product> products) => products.Average(p => (decimal)p.Price); public Expression<Func<IGrouping<string, Product>, decimal>> GetExpression() => group => group.Average(p => (decimal)p.Price); } // 策略字典 var strategyMap = new Dictionary<string, IPriceCalculationStrategy> { ["maxPrice"] = new MaxPriceStrategy(), ["minPrice"] = new MinPriceStrategy(), ["averagePrice"] = new AveragePriceStrategy() }; // 使用时直接取对应策略 var currentStrategy = strategyMap[currentCondition]; // 内存场景 var items = productGroups.Select(g => new Item { Category = g.Key, Price = currentStrategy.Calculate(g) }).ToList(); // ORM场景 var selectExpr = CombineSelect(g => g.Key, currentStrategy.GetExpression()); var items = dbContext.Products.GroupBy(p => p.Category).Select(selectExpr).ToList();
内容的提问来源于stack exchange,提问作者popcorn
相关产品推荐
相关产品推荐

