【问题标题】:C# - Concatenate an in memory IList and IQueryable?C# - 连接内存中的 IList 和 IQueryable?
【发布时间】:2016-09-01 00:25:28
【问题描述】:

假设我有一个包含一个字符串值的列表。假设我还有一个包含来自数据库的多个字符串的 IQueryable。我希望能够将这两个容器连接到一个列表中,然后能够在列表中调用 .Skip 或 .Take 等方法。我希望能够以这样一种方式做到这一点,即当我组合两个容器时,我不会将所有数据库数据加载到内存中(仅在我调用 .Skip 和 .Take 之后)。基本上,我想做这样的事情(伪代码):

IQueryable someQuery = myEntities.GetDBQuery(); // Gets "test2", "test3"
IList inMemoryList = new List();
inMemoryList.Add("test");

IList finalList = inMemoryList.Union(someQuery) // Can I do something like this without loading DB data into memory? finalList should contain all 3 strings.

// At this point it is fine to load the filtered query into memory.
foreach (string myString in finalList.Skip(100).Take(200))
{
   // Do work...
}

我怎样才能做到这一点?

【问题讨论】:

  • 我没有发现这样做有什么问题?你有什么错误吗?
  • 在我创建finalList的那一行,不是所有的DB数据都被加载到内存中了吗(不仅仅是.Skip(100).Take(200)之间的条目)?

标签: c# linq list iqueryable


【解决方案1】:

您指定查询和列表都是字符串序列。 someQuery 可以完全在数据库端执行(不在内存中)

让我们让你的序列不那么通用:

IQueryable<string> someQuery = ...
IList<string> myList = ...

您还指定 myList 仅包含一个元素。

string myOneAndOnlyString = myList.Single();

由于您的列表在内存中,因此必须在内存中执行。但是因为列表只有一个元素,所以这不会花费任何时间。

您请求的查询:

IQueryable<string> correctQuery = someQuery
    .Where(item => item.Equals(myOneandOnlyString)
    .Skip(skipCount)
    .Take(takeCount)

使用您的 SQL 服务器分析器检查使用的 SQL 并查看请求是否在一条 SQL 语句中完全执行。

【讨论】:

    【解决方案2】:

    我完全同意 Danny 的回答,他说您需要以某种方式找到一种方法将内存中的用户列表包含到 db 中,以便实现您想要的。至于您在评论中寻找的示例,在不知道您的用户对象的数据结构的情况下,似乎很困难。但是,假设您能够连接这些点。这是我建议的方法:

    1. 在数据库中创建与常规用户表结构相同的临时表,并将所有内存用户插入其中
    2. 向结构相同的 Union 临时表和常规表编写查询,这样应该很容易。
    3. 在您的应用程序中返回结果并使用它执行标准的 Linq 操作 如果您想要可以按原样使用的确切代码,那么您必须提供您的用户对象结构 - 数据库中的字段类型等,以便我编写代码。

    【讨论】:

      【解决方案3】:

      如果我没理解错,你是在查询数据,一部分来自内存,一部分来自数据库,像这样:

      //the following code will not compile, just for example
      var dbQuery = BuildDbQuery();
      var list = BuildListInMemory();
      var myQuery = (dbQuery + list).OrderBy(aa).Skip(bb).Take(cc).Select(dd);
      //and you don't want to load all records into memory by dbQuery 
      //because you only need some of them
      

      简短的回答是不,你不能。考虑.OrderBy 方法,所有数据必须在同一个“位置”,否则代码无法对它们进行排序。所以代码通过dbQuery将数据库中的所有记录加载到内存中(现在它们在同一个地方),然后对所有记录进行排序,包括list中的记录。当dbQuery 提供数千行时,这可能会导致内存问题。

      如何解决

      1. list 中的数据传入数据库(作为dbQuery 的参数),以便在数据库中进行查询。如果您的 list 只有几个项目,这很容易。

      2. 如果list 也有很多记录会使dbQuery 过于复杂,您可以尝试查询两次,一次查询dbQuery,另一次查询list。例如,您在数据库中有 10,000 个用户,在您的内存列表中有 1,000 个用户,您希望获得前 10 个最年轻的用户。您不需要将 10,000 个用户加载到内存中,然后找到最年轻的 10 个。相反,您在 dbQuery 中找到 10 个最年轻的 (ResultA) 并加载到内存中,在内存列表中找到 10 个最年轻的 (ResultB),然后比较结果A和结果B。

      【讨论】:

      • 你能举一个上面#1的例子吗?实际上,内存中的列表现在在我的应用程序中只包含一个元素(它不是字符串,而是实体对象),但我试图让问题更通用,以便将来可以帮助其他人。这会让事情变得更容易吗?
      • @Andrew 如果你的list 只有一个元素,为什么不直接通过dbQuery 查询呢?为什么要查询dbQuery + 1元素?
      猜你喜欢
      • 1970-01-01
      • 2012-10-20
      • 2011-05-26
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 2014-11-09
      • 1970-01-01
      相关资源
      最近更新 更多