如何通过LINQ按指定字段从MS SQL数据库获取去重数据?
解决LINQ按动态指定字段去重查询的问题
首先说下你原代码的核心问题:e[filterParam]这种动态属性访问在LINQ to Entities里是不被支持的,EF没法把这种写法转换成对应的SQL语句,所以会直接报错。另外,默认的Distinct()是对比整个ScbDataInfo实体的所有属性,除非你重写了实体的Equals和GetHashCode,否则也达不到按指定字段去重的效果。
下面给你两种解决方案,按需选择:
方案1:针对已知字段用Switch-Case(简单易维护)
既然你要筛选的字段只有school和class这几个,直接用switch-case分支处理最直观,也不容易出问题:
如果你要返回按指定字段去重的实体集合
public IEnumerable<ScbDataInfo> GetScbOptionsByFilter(string filterParam) { using (SRSContext entityContext = new SRSContext()) { IQueryable<ScbDataInfo> query = entityContext.Set<ScbDataInfo>(); switch (filterParam.Trim().ToLower()) { case "school": // 按School分组,取每组第一条实现去重 query = query.GroupBy(e => e.school) .Select(g => g.FirstOrDefault()); break; case "class": query = query.GroupBy(e => e.Class) .Select(g => g.FirstOrDefault()); break; default: // 处理无效字段,返回空集合或抛出异常都可以 return Enumerable.Empty<ScbDataInfo>(); } // 按需求排序 query = query.OrderBy(e => e.RefNo).ThenBy(e => e.datepmt); return query.ToArray(); } }
如果你只需要返回指定字段的所有唯一值(比如所有不同的学校名称)
可以修改方法返回类型,直接提取字段值:
public IEnumerable<string> GetDistinctFieldValues(string filterParam) { using (SRSContext entityContext = new SRSContext()) { switch (filterParam.Trim().ToLower()) { case "school": return entityContext.Set<ScbDataInfo>() .Select(e => e.school) .Distinct() .OrderBy(s => s) .ToArray(); case "class": return entityContext.Set<ScbDataInfo>() .Select(e => e.Class) .Distinct() .OrderBy(c => c) .ToArray(); default: return Enumerable.Empty<string>(); } } }
方案2:用表达式树实现动态字段访问(通用方案)
如果以后要支持更多字段,不想写一堆switch-case,可以用表达式树动态构建查询逻辑,这样不管加多少字段都不用改查询代码:
using System.Reflection; using System.Linq.Expressions; public IEnumerable<ScbDataInfo> GetScbOptionsByFilter(string filterParam) { using (SRSContext entityContext = new SRSContext()) { // 反射获取指定字段的属性信息,忽略大小写 var property = typeof(ScbDataInfo).GetProperty( filterParam, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (property == null) { return Enumerable.Empty<ScbDataInfo>(); } // 构建动态访问属性的表达式 var param = Expression.Parameter(typeof(ScbDataInfo), "e"); var propertyAccess = Expression.MakeMemberAccess(param, property); var keySelector = Expression.Lambda<Func<ScbDataInfo, object>>( Expression.Convert(propertyAccess, typeof(object)), param); // 执行分组去重+排序 var query = entityContext.Set<ScbDataInfo>() .GroupBy(keySelector) .Select(g => g.FirstOrDefault()) .OrderBy(e => e.RefNo) .ThenBy(e => e.datepmt); return query.ToArray(); } }
额外说明
- 关于
GroupBy替代Distinct:因为默认的Distinct()会对比实体的所有属性,用GroupBy按目标字段分组后取第一条,能更精准地实现按指定字段去重的需求。 - 大小写兼容:代码里都做了大小写处理,不管你传入的是
School还是school都能正常识别。
内容的提问来源于stack exchange,提问作者Ande Caleb
相关产品推荐
相关产品推荐

