【问题标题】:A simple join consumes too much memory - LINQ一个简单的连接消耗太多内存 - LINQ
【发布时间】:2017-05-15 07:49:55
【问题描述】:

我有这个加入:

var andlist = (from cust in custFinal
               join serv in db.Service on cust.ID equals serv.CustID
               select new JoinObj
               {
                   Name = cust.name,
                   ServiceID = serv.ID,
               });

custFinalCustomers 的列表,其中仅包含一个对象。 db.Service 是一个 DbSet,在 Service 表中只有四行 custID 等于客户对象的 ID。当我使用ToList()Count() 时,使用的内存很快就超过了1GB,并且出现了outOfMemory 异常。你能告诉我这段代码有什么问题吗?提前致谢。

【问题讨论】:

  • 那是哪个版本的实体框架? EF 核心?
  • @Evk 它是 6.1.3
  • 有多少行数据
  • @LeiYang 在Service表中大约有110万行。
  • 加入 100 万行对您来说很简单吗?

标签: c# linq join out-of-memory


【解决方案1】:

原因是您并没有真正在服务器上执行连接。 custFinal 正如你所说的只是内存列表,而不是数据库表或查询。所以它是IEnumerable,而不是IQueryable。当您执行连接时 - 它调用 IEnumerable.Join,而不是 IQueryable.Join 方法。后者将构建一个查询,但前者只会将所有参数拉入内存并在内存中执行连接。所以结果 - 整个 Service 表被拉入内存并加入其中(很容易检查您是否记录了 EF 上下文查询 - 您会看到它只是从服务查询中执行全选)。

如果您更改连接中参数的顺序以便执行IQueryable.Join - 这也无济于事,因为无论如何您都无法使用实体框架将数据库表与内存列表连接起来。所以你必须另想办法,例如:

var ids = custFinal.Select(c => c.ID).ToArray();
var matchingServices = db.Service.Where(serv => ids.Contains(serv.CustID)).Select(c => new {c.ServiceID, c.CustID}).ToArray();
// now filter `custFinal` based on `matchingServices`, in memory.

这将执行CustID IN (...) 查询而不是连接。如果您坚持要加入 - 您将不得不使用原始 sql 执行此操作,而无需实体框架(如果您使用 SQL server,您还需要在 sql server 中创建自定义表类型)。

【讨论】:

  • 有没有办法将List 转换回IEnumerable?谢谢。
  • 不确定我是否理解这个问题。 List 已经是 IEnumerable
  • 我的意思是如果我使用custFinal 作为IEnumerable,它可以工作,那么有没有办法转换?
  • 如果 custFinalList<Customer> - 它已经是 IEnumerable。如果你愿意,你可以直接投射它:(IEnumerable<Customer>) custFinal(虽然不知道为什么这样做)。
  • IEnumerable 是所有可以枚举的非泛型集合的基本接口。 IEnumerable 包含一个方法 GetEnumerator,它返回一个 IEnumerator。因此,如果您的代码适用于它,它应该适用于列表。如果不是,则序列中可能还有其他错误。
猜你喜欢
  • 1970-01-01
  • 2020-04-16
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-16
  • 2016-06-29
相关资源
最近更新 更多