案例1:
我在两个上下文中通过ToList()
方法加入了两个不同的DB Context。
案例2:
并尝试使用ToList()
加入第一个Db上下文,使用AsQueryable()
加入第二个Db上下文。
两者都适合我。我想知道的是那些关于性能和功能的加入之间的区别。哪一个更好 ?
var users = (from usr in dbContext.User.AsNoTracking()
select new
{
usr.UserId,
usr.UserName
}).ToList();
var logInfo= (from log in dbContext1.LogInfo.AsNoTracking()
select new
{
log.UserId,
log.LogInformation
}).AsQueryable();
var finalQuery= (from usr in users
join log in logInfo on usr.UserId equals log.UserId
select new
{
usr.UserName,
log.LogInformation
}.ToList();
答案 0 :(得分:10)
我将详细阐述Jehof在评论中给出的答案。确实,这个连接将在内存中执行。它有两个原因发生。
首先,无法在数据库中执行此连接,因为您正在使用延迟查询(users
)加入内存中的对象(logInfo
)。基于此,无法生成可以发送到数据库的查询。这意味着在执行实际连接之前,将执行延迟查询,并从数据库中检索所有日志。总而言之,在这种情况下,在数据库中执行2个查询,并在内存中进行连接。 在这种情况下使用 ToList + AsQueryable 或 ToList + ToList 并不重要。
其次,在您的方案中,此连接只能在内存中执行。即使您将AsQueryable
与第一个上下文一起使用,也使用第二个上下文,它将无效。您将收到System.NotSupportedException
例外消息:
指定的LINQ表达式包含对与不同上下文关联的查询的引用。
我想知道你为什么要使用2个DB上下文。真的需要吗?正如我所解释的,因为你失去了充分利用延迟查询(懒惰评估功能)的可能性。
如果你真的必须使用2个DB上下文,我会考虑为负责从DB读取用户和日志的查询添加一些过滤器(WHERE条件)。为什么?对于少量记录,没有问题。但是,对于大量数据,在内存中执行连接效率不高。为此,创建了数据库。
答案 1 :(得分:1)
还没有解释为什么语句实际起作用以及EF为什么不抛出一个只能在LINQ语句中使用基本类型序列的异常。
如果您更换两个列表......
var finalQuery= (from log in logInfo
join usr in users on log.UserId equals usr.UserId
...
EF将抛出
无法创建类型' User'的常量值。在此上下文中仅支持原始类型或枚举类型。
为什么你的代码有效?
如果我们将您的语句转换为方法语法(运行时在引擎盖下),那将会变得清晰:
users.Join(logInfo, usr => usr.UserId, log => log.UserId
(usr,log) => new
{
usr.UserName,
log.LogInformation
}
由于users
是IEnumerable
,因此扩展方法Enumerable.Join
将被解析为适当的方法。此方法接受IEnumerable
作为要加入的第二个列表。因此,logInfo
被隐式强制转换为IEnumerable
,因此它在参与连接之前作为单独的SQL语句运行。
在版本from log in logInfo join usr ...
中,使用了Queryable.Join
。现在usr
已转换为IQueryable
。这会将整个语句转换为EF未成功尝试转换为一个SQL语句的表达式。
现在谈谈
哪一个更好?
最好的选择就是能让它发挥作用的选择。这意味着
AsQueryable()
,因为logInfo
已经是IQueryable
,并且无论如何都会被转换为IEnumerable
。ToList()
替换为AsEnumerable()
,因为ToList()
会构建冗余的中间结果,而AsEnumerable()
只会更改users
的运行时类型,而不会触发它的执行还没有。