使用ToList()和.AsQueryable()连接两个不同的DB上下文有什么区别?

时间:2015-11-23 08:00:19

标签: c# sql linq

案例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();

2 个答案:

答案 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
                        }

由于usersIEnumerable,因此扩展方法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的运行时类型,而不会触发它的执行还没有。