EF Linq嵌套查询报错及单期刊关联收件人/读者查询方案咨询
问题:EF LINQ查询返回单条期刊数据,关联唯一收件人和所有读者
我在编写嵌套LINQ查询时遇到了问题,需要关联4张数据表:期刊(Journal)对应唯一收件人(Recipient)和多个读者(Reader),期望展示单条期刊数据及其关联的收件人与所有读者。
以下是我的EF查询代码:
var myJournals = ( from s in db.Journals where !s.Blacklist join recToJournals in db.RecipientsToJournals on s.JournalID equals recToJournals.JournalID join recipients in db.Recipients on recToJournals.RecipientID equals recipients.RecipientID join reaToJournals in db.ReadersToJournals on s.JournalID equals reaToJournals.JournalID join readers in db.Readers on reaToJournals.ReaderID equals readers.ReaderID select new AnalysisViewModel { JournalID = s.JournalID, Title = s.Title, RecipientName = recipients.FullName, ReaderList = readers.FullName.ToList() }); return View(myJournals);
ViewModel代码:
public class AnalysisViewModel { public int JournalID { get; set; } public string Title { get; set; } public List<Char> ReaderList { get; set; } public string ReaderName { get; set; } public string RecipientName { get; set; } }
执行时抛出异常:
System.NotSupportedException: The method 'ToList' is not supported when called on an instance of type 'String'。
如果改用ReaderName = readers.FullName则可运行,但会生成多条重复期刊数据(每条对应一个读者)。请问如何实现单条期刊数据关联其收件人和所有读者的查询?
解决方案
第一步:修复ViewModel的类型错误
首先,你的AnalysisViewModel里ReaderList的类型完全错了——List<Char>会把单个读者姓名拆成字符数组,而你需要的是所有读者姓名的字符串集合,应该改成List<string>,同时可以去掉多余的ReaderName字段:
public class AnalysisViewModel { public int JournalID { get; set; } public string Title { get; set; } public List<string> ReaderList { get; set; } // 修正为字符串列表 public string RecipientName { get; set; } }
第二步:调整LINQ查询,通过分组聚合读者列表
你当前的多表join会生成笛卡尔积(一个期刊+一个收件人+一个读者就生成一条记录),所以必须通过GroupBy按期刊维度分组,把同一个期刊的所有读者姓名聚合到列表中。
方法1:使用GroupBy的LINQ查询
var myJournals = ( from s in db.Journals where !s.Blacklist // 关联唯一收件人(确保一个期刊对应一条收件人记录) join recToJournals in db.RecipientsToJournals on s.JournalID equals recToJournals.JournalID join recipients in db.Recipients on recToJournals.RecipientID equals recipients.RecipientID // 关联读者表,用into语法处理可能无读者的情况 join reaToJournals in db.ReadersToJournals on s.JournalID equals reaToJournals.JournalID into readerJoin from rtj in readerJoin.DefaultIfEmpty() join readers in db.Readers on rtj?.ReaderID equals readers.ReaderID into readerGroup from r in readerGroup.DefaultIfEmpty() // 按期刊核心信息分组,聚合读者数据 group new { r } by new { s.JournalID, s.Title, recipients.FullName } into g select new AnalysisViewModel { JournalID = g.Key.JournalID, Title = g.Key.Title, RecipientName = g.Key.FullName, // 过滤空值,提取所有读者姓名并转成列表 ReaderList = g.Where(x => x.r != null).Select(x => x.r.FullName).ToList() }).ToList(); return View(myJournals);
方法2:利用EF导航属性(更简洁)
如果你的EF实体模型已经配置了导航属性(比如Journal类里包含Recipient单例导航和Readers集合导航),可以完全跳过手动join,直接用导航属性查询:
var myJournals = db.Journals .Where(s => !s.Blacklist) .Select(s => new AnalysisViewModel { JournalID = s.JournalID, Title = s.Title, RecipientName = s.Recipient.FullName, // 假设Journal有Recipient导航属性 ReaderList = s.Readers.Select(r => r.FullName).ToList() // 假设Journal有Readers导航集合 }).ToList(); return View(myJournals);
关键说明
- 你之前的错误根源是调用了
readers.FullName.ToList():FullName是字符串类型,ToList()会把它拆成单个字符的集合,EF不支持在查询中对字符串执行这个操作,而且这也不是你需要的读者列表。 - 使用
GroupBy可以把笛卡尔积生成的多条重复期刊记录合并成单条,同时聚合所有关联的读者姓名。 - 加入
DefaultIfEmpty()是为了兼容没有读者的期刊,避免这类数据被过滤掉。
内容的提问来源于stack exchange,提问作者exmachina
相关产品推荐
相关产品推荐

