LINQ 查询具有相同逻辑的不同集合

本文关键字:集合 LINQ 查询 | 更新日期: 2023-09-27 18:35:15

这个问题已经困扰了我一段时间了,让我分解一下。

我需要查询两个逻辑相同的WhereSelect语句和事物的不同集合,我事先不知道这个逻辑。假设我有两个集合:

var arrayOne = {"Germane", "Henry", "Charissa", "Evan", "Zorita"};
var arrayTwo = {"Athena", "Darryl", "Zelenia", "Honorato", "Macon"};

还有一些过滤逻辑:

var result = arrayOne.Where(x => x.Length > 3).Select(x => x.Length);

如何在不复制代码本身的情况下将此逻辑复制到第二个数组?有一种方法可以通过代表(这是我当前的代表)来做到这一点

Func<IEnumerable<string>, IEnumerable<int>> query = x => x.Where(x => x.Length > 3).Select(x => x.Length);
var result1 = query(arrayOne);
var result2 = query(arrayTwo );

但是我需要逐步构建查询,而不是内联,例如

var query = SomeQueryGenerator.CreateQuery();
query.Where(x => x.Length > 3)
if(someCondition)
{
    // Totally random query
    query.Where(x => x.Length % 2 == 0);
} 
var result = query(arrayOne);

这就是问题所在。现在让我展示一下到目前为止我所做的事情(并进一步分解我的情况)

当我们尝试将大量数据导入数据库时,出现了问题。此数据大约有 20k 行,并且可能包含一些重复项(我们不想存储),因此检查我们是否已经有特定的行(它通过一些合法 ID 进行检查)非常重要。我们使用 NHibernate 作为 ORM,默认情况下,它经常刷新 INSERT s 和 UPDATE s,在每个 SELECT 语句之前或任何其他时间刷新。

问题是这些刷新需要很长时间,以至于将所有 20k 行加载到数据库中最多需要 10 个小时。最明显的方法是将我们当前的Session.FlushMode设置为FlushMode.Never并在导入结束时刷新它。这样 20k 行导入大约 10 分钟。但问题是,这样当我们查询时,我们看不到我们的重复项,因为它们还没有在数据库中,而且我们的查询像.Where(x => x.LegalId == someId)即使实际上有一个保存的行也找不到任何东西。我决定利用我们的 Session 一级缓存来完成此任务,因为我们所有保存的 entites 首先写入它的缓存,然后在Flush发生时写入 DB(它可能实际上不会写入数据库,直到我们的transaction.Commit,但它会通过查询正确找到这些入口)。我已经设置了一个可以查询缓存和原始数据库的帮助程序类,它可以正确查找缓存中尚未在数据库中的ent(上面的内联委托示例)

但是我需要能够逐步查询事物,而我现在完全迷失了。我们不能只是从一个IQueryable获取Expression并将其粘贴到另一个。我们不能创建一些虚拟IQueryableIEnumerable对象并传递它,抛出.Where.Select,因为我们将被绑定到特定的来源,我们也不能交换来源(至少我没有找到一种方法)。

我需要对这个问题有一些新的想法,提前谢谢。

-- 关于数据重复的最新情况

我们无法预处理我们的数据,让我解释一下原因。

假设我们的文件中有建筑物。某些构建可以属于组织。如果我们遇到具有指定组织的建筑物,则需要创建它并存储在数据库中。这样,我们不仅拥有建筑物(它们本身不重复),而且还拥有它们的组织。几座建筑物完全有可能具有相同的组织,但我们不需要多次创建这些组织。当然,我们可以先解析这个文件,获取所有组织,先导入它们,然后只导入构造,但这太复杂了,而且,正如我在评论中所说,问题更通用,它超出了这个导入逻辑的范围

LINQ 查询具有相同逻辑的不同集合

仍然不太确定您要确切地做什么,但我想这可以帮助您朝着正确的方向前进:

    var arrayOne = new []{"Germane", "Henry", "Charissa", "Evan", "Zorita"};
    var arrayTwo = new []{"Athena", "Darryl", "Zelenia", "Honorato", "Macon"};
    //var result1 = arrayOne.Where(x => x.Length > 3).Select(x => x.Length);
    bool condition1 = true;
    bool condition2 = false;
    bool condition3 = true;
    var fnc = (Func<IEnumerable, IEnumerable>)(source =>
    {
        var qry = source;
        if (condition1)
        {
            qry = qry.Cast<string>().Where(x => x.Length > 3);
        }
        if (condition2)
        {
            qry = qry.Cast<int>().Where(x => x < 10);
        }
        if (condition3)
        {
            return qry.Cast<string>().Select(x => x.Length);
        }
        return qry;
    });
    var result_v2_1 = fnc(arrayOne);
    var result_v2_2 = fnc(arrayTwo);

请注意,将 consition1 和条件 2 设置为 true 将导致错误。 另请注意,条件 2 与 String[] 的输入相结合也会导致错误。

选项 1:更改源

尽管 LINQ 查询绑定到序列,但可以更改该序列并重新计算查询。所以你可以做这种事情:

var arrayOne = new[] {"Germane", "Henry", "Charissa", "Evan", "Zorita"};
var arrayTwo = new[] {"Athena", "Darryl", "Zelenia", "Honorato", "Macon"};
// bind to an empty sequence to start with
var source = new List<string>();
var query = source.Where (x => x.Length > 3);
if (true)
{
    query = query.Where (x => x.Length % 2 == 0);
}
// change the sequence once
source.AddRange(arrayOne);
var resultOne = query.ToArray();    
Console.WriteLine(string.Join(", ", resultOne));
// change the sequence again
source.Clear();
source.AddRange(arrayTwo);
var resultTwo = query.ToList();
Console.WriteLine(string.Join(", ", resultTwo));

第一个Console.WriteLine输出以下内容:

查丽莎、埃文、佐里塔

第二个输出这个:

雅典娜、达里尔、霍诺拉托

选项 2:使用方法

更简单的解决方案是为这些序列操作中的任何一个创建方法,而不是尝试以内联方式完成所有操作:

private static IEnumerable<string> Filter(IEnumerable<string> sequence)
{
    var bigOnes = sequence.Where (s => s.Length > 3);
    if (true)
    {
        bigOnes = bigOnes.Where (s =>  s.Length % 2 == 0);
    }
    return bigOnes;
}

var resultThree = Filter(arrayOne);  
Console.WriteLine(string.Join(", ", resultThree));
var resultFour = Filter(arrayTwo);
Console.WriteLine (string.Join(", ", resultFour));