LINQ Lambda报错:指定表达式引用不同上下文关联查询如何解决
错误原因
你遇到的报错是因为LINQ to Entities会尝试将整个查询表达式翻译为SQL执行,你的表达式中同时引用了Db1Context和Db2Context两个不同的上下文实例,EF无法跨不同上下文生成对应的SQL语句,因此触发错误。
可行解决方案
方案1:先加载消息数据到内存再匹配用户名(适合小数据量场景)
先调用ToList()将Db2的Messages数据全部加载到内存中,后续的用户匹配操作会在内存执行,不再涉及EF的SQL翻译逻辑:
using (var contextDb1 = new Db1Context(System.Web.HttpContext.Current.Session["DB1ConnectionString"].ToString(), false)) { using (var contextDb2 = new Db2Context(System.Web.HttpContext.Current.Session["DB2ConnectionString"].ToString(), false)) { // 先把消息数据加载到内存 var messages = contextDb2.Messages.ToList(); var messagesList = messages .Select(m => new MessagesViewModel { // 内存中匹配用户名,增加判空避免空引用异常 UserName = contextDb1.UsersInfo.FirstOrDefault(u=>u.Id == m.UserId)?.UserName ?? string.Empty, MessageId = m.MessageId, MessageText = m.MessageText, DateTime = m.DateTime }) .ToList(); return messagesList; } }
注意:如果Messages表数据量很大,全量加载到内存会占用过多内存,不推荐用这个方案。
方案2:预加载用户字典批量匹配(适合中大数据量场景)
先把需要用到的用户Id和用户名从Db1中查询出来生成字典,再和消息数据匹配,避免每次循环都访问Db1:
using (var contextDb1 = new Db1Context(System.Web.HttpContext.Current.Session["DB1ConnectionString"].ToString(), false)) { using (var contextDb2 = new Db2Context(System.Web.HttpContext.Current.Session["DB2ConnectionString"].ToString(), false)) { var messages = contextDb2.Messages.ToList(); // 提取所有消息关联的用户Id var userIds = messages.Select(m => m.UserId).Distinct().ToList(); // 预查询所有需要的用户信息生成字典 var userDict = contextDb1.UsersInfo .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => u.UserName); var messagesList = messages .Select(m => new MessagesViewModel { UserName = userDict.TryGetValue(m.UserId, out var userName) ? userName : string.Empty, MessageId = m.MessageId, MessageText = m.MessageText, DateTime = m.DateTime }) .ToList(); return messagesList; } }
方案3:跨库表映射(两个数据库在同一服务器且有权限的最优方案)
如果Db1和Db2部署在同一个数据库实例下,且账号有访问两个库的权限,可以直接在其中一个DbContext中映射另一个库的表,不需要创建两个上下文,直接在LINQ中做关联查询,性能最高:
- 在Db2Context中添加UsersInfo的实体映射,指定表的全路径(比如
[Db1].[dbo].[UsersInfo]) - 直接用一个上下文做关联查询:
using (var contextDb2 = new Db2Context(System.Web.HttpContext.Current.Session["DB2ConnectionString"].ToString(), false)) { var messagesList = contextDb2.Messages .Join(contextDb2.UsersInfo, m => m.UserId, u => u.Id, (m, u) => new MessagesViewModel { UserName = u.UserName, MessageId = m.MessageId, MessageText = m.MessageText, DateTime = m.DateTime }) .ToList(); return messagesList; }
内容的提问来源于stack exchange,提问作者Nadorkhan
相关产品推荐
相关产品推荐

