C# 使用ToList()和.AsQueryable()连接两个不同的DB上下文有什么区别?
案例1: 在这两个上下文中,我通过C# 使用ToList()和.AsQueryable()连接两个不同的DB上下文有什么区别?,c#,sql,linq,C#,Sql,Linq,案例1: 在这两个上下文中,我通过ToList()方法连接了两个不同的DB上下文 案例2: 还尝试将第一个Db上下文与ToList()连接,第二个与AsQueryable()连接 两个人都为我工作。我只想知道这些连接在性能和功能上的区别。哪一个更好 var users = (from usr in dbContext.User.AsNoTracking() select new {
ToList()
方法连接了两个不同的DB上下文
案例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();
我将详细说明杰霍夫在评论中给出的答案。确实,此联接将在内存中执行。这有两个原因 首先,无法在数据库中执行此联接,因为您正在使用延迟查询(
logInfo
)联接内存中的对象(users
)。基于此,不可能生成可以发送到数据库的查询。这意味着在执行实际连接之前,将执行一个延迟查询,并从数据库检索所有日志。总之,在这个场景中,2个查询在数据库中执行,连接在内存中发生在这种情况下,使用ToList+AsQueryable或ToList+ToList并不重要。
其次,在您的场景中,这种连接只能在内存中执行。即使将AsQueryable
与第一个上下文和第二个上下文一起使用,它也不会起作用。您将获得System.NotSupportedException
异常,并显示以下消息:
指定的LINQ表达式包含对与不同上下文关联的查询的引用
我想知道你为什么要使用2db上下文。真的需要吗?正如我所解释的那样,您失去了充分利用延迟查询(惰性评估特性)的可能性
如果您真的必须使用2个DB上下文,我会考虑添加一些过滤器(其中条件)来查询负责从DB读取用户和日志的查询。为什么?对于少量记录,没有问题。但是,对于大量数据,在内存中执行联接是不高效的。为此目的,创建了数据库。
ToList()- 立即执行查询
- 您将在内存中准备好所有元素
- 懒惰(稍后执行查询)
- 参数:
表达式
- 将表达式转换为T-SQL(使用特定的提供程序),远程查询并将结果加载到应用程序内存中
- 这就是为什么DbSet(在实体框架中)也继承IQueryable以获得高效查询
- 它不会加载所有记录。例如,如果采用(5),它将在后台生成select top 5*SQL
var finalQuery= (from log in logInfo
join usr in users on log.UserId equals usr.UserId
...
EF将抛出
无法创建“用户”类型的常量值。在此上下文中仅支持基元类型或枚举类型
那么为什么你的代码可以工作呢
如果我们将您的语句转换为方法语法(运行时在后台执行此操作),这一点就会变得很清楚:
由于users
是一个IEnumerable
,因此扩展方法被解析为合适的方法。此方法接受一个IEnumerable
作为要联接的第二个列表。因此,logInfo
被隐式转换为IEnumerable
,因此它在参与联接之前作为单独的SQL语句运行
在登录logInfo加入usr的版本中使用…。现在usr
被转换为IQueryable
。这会将整个语句转换为一个表达式,EF试图将该表达式转换为一个SQL语句,但未成功
现在请讲几句
哪一个更好
最好的选择是做得足够好的选择。也就是说
- 您可以删除
,因为AsQueryable()
已经是一个logInfo
并且它被强制转换为IQueryable
IEnumerable
- 您可以将
替换为ToList()
,因为AsEnumerable()
构建了一个冗余的中间结果,而ToList()
只更改AsEnumerable()
用户的运行时类型,而不触发其执行
IQueryable
是非常好的。请注意,您可以同样轻松地使用.ToList().AsQueryable()
——结果是列表中的一个可查询项。但是你仍然必须首先得到整个列表:)@Arthik你是对的,logInfo的类型在加入后不会改变。在C#中,在声明变量类型后不能更改它。logInfo被“转换”为场景后面的列表,此操作的结果不会存储在代码中的任何变量中。@Michal,“场景后面”的意思是,这是否发生在任何临时内存中?内存保存列表的时间有多长?@Arthik它出现在应用程序使用的内存(RAM)中。当不再使用此列表时,即没有指向此列表的引用时,垃圾回收会将其从内存中删除。这并不能回答问题
users.Join(logInfo, usr => usr.UserId, log => log.UserId
(usr,log) => new
{
usr.UserName,
log.LogInformation
}