如何将SQL查询结果映射到含接口参数构造函数的C#类(EF6转原生SQL)
解决方案
方案1:SQL端直接聚合(推荐,性能最优)
无需硬凑IGrouping参数的构造函数,直接在数据库层面完成分组统计,避免冗余数据传输,是原生SQL实现的最优选择。
- 改写SQL为分组统计语句:
SELECT dsm.Name, CONCAT(ds.VersionMajor, '.', ds.VersionMinor, '.', ds.VersionPatch) AS Version, COUNT(ld.LogDownloadId) AS Count FROM LogDownloads ld INNER JOIN DiagSofts ds ON ld.DiagSoftId = ds.DiagSoftId INNER JOIN DiagSoftMarks dsm ON ds.DiagSoftMarkId = dsm.DiagSoftMarkId WHERE ld.MemberShipId = @MemberShipId GROUP BY dsm.Name, ds.VersionMajor, ds.VersionMinor, ds.VersionPatch ORDER BY Count DESC, Name ASC, Version ASC
- 给
DiagSoftDownload新增无参构造函数兼容映射,不影响原有逻辑:
public class DiagSoftDownload { // 保留原有构造函数 public DiagSoftDownload(IGrouping<DiagSoft, LogDownload> data) { Name = data.Key.DiagSoftMark.Name; Version = data.Key.Version; Count = data.Count(); } // 新增无参构造用于映射 public DiagSoftDownload() {} public string Name { get; set; } public string Version { get; set; } public int Count { get; set; } }
- Dapper查询代码:
using (var conn = new SqlConnection("你的数据库连接字符串")) { var result = conn.Query<DiagSoftDownload>( "上面的分组SQL语句", new { MemberShipId = VM.SelectedMembershipLog.MembershipId } ).ToList(); }
方案2:保留原有逻辑,内存分组
如果不想修改原有DiagSoftDownload类的定义,可以先查全量关联数据,在内存中完成分组后实例化对象:
- 用Dapper多映射功能关联查询三个表的实体:
using (var conn = new SqlConnection("你的数据库连接字符串")) { var sql = "你从EF提取的原SQL,将WHERE条件的447替换为参数@MemberShipId"; // 拆分行数据到三个关联实体 var logDownloads = conn.Query<LogDownload, DiagSoft, DiagSoftMark, LogDownload>( sql, (log, soft, mark) => { soft.DiagSoftMark = mark; log.DiagSoft = soft; return log; }, splitOn: "DiagSoftId,DiagSoftMarkId", // 按这两个字段拆分列到对应实体 param: new { MemberShipId = VM.SelectedMembershipLog.MembershipId } ).ToList(); // 内存分组后实例化目标类,和原EF逻辑完全一致 var result = logDownloads // 如果DiagSoft没有重写Equals/HashCode,可改为GroupBy(m=>m.DiagSoft.DiagSoftId) .GroupBy(m => m.DiagSoft) .Select(g => new DiagSoftDownload(g)) .OrderByDescending(m => m.Count) .ThenBy(m => m.Name) .ThenBy(m => m.Version) .ToList(); }
如果按DiagSoftId分组,需要自己实现简单的IGrouping实现类:
public class Grouping<TKey, TElement> : IGrouping<TKey, TElement> { public TKey Key { get; } public IEnumerable<TElement> Elements { get; } public Grouping(TKey key, IEnumerable<TElement> elements) { Key = key; Elements = elements; } public IEnumerator<TElement> GetEnumerator() => Elements.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
报错原因说明
之前用Dapper/ SqlDataReaderMapper直接映射报错,是因为这些ORM默认只能构造参数为基础类型的实例,你原来的构造函数参数是IGrouping<DiagSoft, LogDownload>这种复杂接口类型,ORM无法直接从查询列生成对应的实例,所以需要先拿到基础数据后自行构造。
内容的提问来源于stack exchange,提问作者Lion
相关产品推荐
相关产品推荐

