【问题标题】:How to join a list and large lists/tables using LINQ如何使用 LINQ 加入列表和大型列表/表
【发布时间】:2017-06-30 06:58:40
【问题描述】:

最初我有这样一个列表:

List<Car> cars = db.Car.Where(x => x.ProductionYear == 2005).ToList();

然后我尝试使用这样的 LINQ 将两个大表加入这个列表:

var joinedList = (from car in cars
                  join driver in db.Driver.ToList() 
                    on car.Id equals driver.CarId
                  join building in db.Building.ToList() 
                    on driver.BuildingId equals building.Id
                  select new Building
                  {
                     Name = building.Name;
                     Id = building.Id;
                     City = building.City;
                  }).ToList();

DriverBuilding 表都有大约 100 万行。当我运行此连接时,我会出现内存不足异常。我怎样才能使这个加入工作?我应该对数据库进行连接操作吗?如果是,我如何将cars 列表带到数据库?提前致谢。

【问题讨论】:

  • db.Driver.ToList() 从这里删除ToList(),它将阻止获取内存中的整个驱动程序表。同样来自db.Building
  • @RahulSingh 我这样做了,但我仍然得到异常。
  • 你打算用这些收藏做什么?
  • 是的,如果您的 join 仍然产生数十亿条记录,那将无济于事。您应该分批过滤和获取数据(如果在 UI 中显示此数据或其他内容)。
  • @GiladGreen 我正在尝试为每辆车获取相应的建筑物。

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


【解决方案1】:

即使您删除了联接中的 .ToList() 调用,您的代码仍将提取所有数据并在内存中执行联接,而不是在 SQL Server 中。这是因为您在加入时使用了本地列表 cars。以下应该可以解决您的问题:

var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005)
                  join driver in db.Driver 
                    on car.Id equals driver.CarId
                  join building in db.Building 
                    on driver.BuildingId equals building.Id
                  select new Building
                  {
                     Name = building.Name;
                     Id = building.Id;
                     City = building.City;
                  }).ToList();

如果您希望结果中的记录过多,您可以删除最后一个 .ToList() 并做一些 paging

【讨论】:

  • 现在它使用更少的内存,但它仍然消耗超过 1 GB 的内存。没有办法减少这种情况吗?在我完成加入后有没有办法释放内存?感谢您的回答。
  • @jason joinedList 应该是上述代码执行后您需要担心的一切。所有不再使用的东西都被标记为垃圾回收(并且将在将来触发 GC 时被回收)。但是,如果joinedList 的项目太多,请检查我的答案中的最后一行并实现分页。
  • 在一种情况下,我在该连接中遇到了内存不足异常。我做了你所做的。
  • @jason 上面的答案解决了db.Driverdb.Buildingdb.Card 有太多记录的问题,而你将这些记录拉到内存中是因为你做错了join方式。但是,如果返回结果是一个不适合内存的巨大列表,那么分页就是解决方案。请记住,select new Building 正在内存中创建对象,如果你有太多(我猜超过 2 GB,你会得到内存不足异常)分页通过按需创建部分对象来解决这个问题。跨度>
  • 我不知道为什么,但是当我在表格后添加AsQueryable() 时,它的工作效率非常高。
【解决方案2】:

即使您已删除 .ToList() 替换 .AsQueryable()

AsQueryable 比 ToList 和 AsEnumerable 更快

  • 如果创建 IQueryable,则查询可能会转换为 sql 并在数据库服务器上运行

  • 如果创建 IEnumerable,则所有行都将被拉入
    在运行查询之前将内存作为对象。

  • 在这两种情况下,如果您不调用 ToList() 或 ToArray() 则查询
    每次使用时都会执行,所以,比如说,你有一个
    IQueryable 并从中填充 4 个列表框,然后查询将是 对数据库运行 4 次。

所以跟随使用的 Linq 查询

var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005).AsQueryable()
              join driver in db.Driver.AsQueryable() 
                on car.Id equals driver.CarId
              join building in db.Building.AsQueryable() 
                on driver.BuildingId equals building.Id
              select new Building
              {
                 Name = building.Name,
                 Id = building.Id,
                 City = building.City,
              }).ToList();

【讨论】:

  • db.Driverdb.Driver.AsQueryable有什么区别?
【解决方案3】:

首先不要在使用 LINQ 时尝试 ToList()(可以),但请确保仅在非常罕见的情况下尽可能少地使用 ToList()。 每次当表包含许多行时,您都会得到OutOfMemoryException。 因此,这是您问题的代码:

var joinedList = (from car in db.Car.GetQueryable().Where(x => x.ProductionYear == 2005)
              join driver in db.Driver.GetQueryable() on car.Id equals driver.CarId
              join building in db.Building.GetQueryable() on driver.BuildingId equals building.Id
              select new Building
              {
                 Name = building.Name;
                 Id = building.Id;
                 City = building.City;
              }).ToList();

【讨论】:

  • 在非常罕见的情况下 ..这是错误的。如果您遵循影响性能并进行往返的建议,您可以进行多次枚举。好的建议是了解延迟执行,然后您就可以知道何时使用.ToList(),何时不使用。
  • 是的,我完全同意你的观点,如果你是优秀的编码员,那么在了解执行后,你就会知道何时使用以及在哪里使用。有时它会根据业务逻辑有所帮助。
猜你喜欢
  • 2019-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
相关资源
最近更新 更多