情况1:
我加入了两个不同的DB Context by ToList()
两种语境中的方法。
案例2:
并尝试加入第一个Db上下文 ToList()
第二个 AsQueryable()
。
两者都适合我。我想知道的是那些关于性能和功能的加入之间的区别。哪一个更好 ?
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();
我将详细阐述Jehof在评论中给出的答案。确实,这个连接将在内存中执行。它有两个原因发生。
首先,无法在数据库中执行此连接,因为您正在加入内存中的对象(users
)使用延迟查询(logInfo
)。基于此,无法生成可以发送到数据库的查询。这意味着在执行实际连接之前,将执行延迟查询,并从数据库中检索所有日志。总而言之,在这种情况下,2个查询在数据库中执行,并且连接发生在内存中。 如果你使用它并不重要 ToList + AsQueryable 要么 ToList + ToList 在这种情况下。
其次,在您的场景中,此连接只能在内存中执行。即使你使用 AsQueryable
对于第一个上下文和第二个上下文,它将无法工作。你会得到 System.NotSupportedException
消息的异常:
指定的LINQ表达式包含对与不同上下文关联的查询的引用。
我想知道你为什么要使用2个DB上下文。真的需要吗?正如我所解释的那样,你失去了充分利用延迟查询(懒惰评估功能)的可能性。
如果你真的必须使用2个DB上下文,我会考虑为负责从DB读取用户和日志的查询添加一些过滤器(WHERE条件)。为什么?对于少量记录,没有问题。但是,对于大量数据,在内存中执行连接效率不高。为此,创建了数据库。
还没有解释为什么语句实际工作以及为什么EF不会抛出一个异常,你只能在LINQ语句中使用原始类型的序列。
如果你交换两个列表......
var finalQuery= (from log in logInfo
join usr in users on log.UserId equals usr.UserId
...
EF将投掷
无法创建“用户”类型的常量值。在此上下文中仅支持原始类型或枚举类型。
那么为什么你的代码有效呢?
如果我们将您的语句转换为方法语法(运行时在引擎盖下),这将变得清晰:
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
,尚未触发其执行。