c# - 搜索可枚举的最有效方法

标签 c# performance linq dynamics-crm processing-efficiency

我正在编写一个小程序,它接受一个 .csv 文件作为输入,其中包含大约 45k 行。我正在尝试将此文件的内容与数据库表的内容进行比较(SQL Server 通过使用 Xrm.Sdk 的动态 CRM,如果它有所不同)。

在我当前的程序中(比较需要大约 25 分钟 - 文件和数据库在这两个 45k 行中完全相同,没有差异),我在 DataCollection<Entity> 中拥有数据库中的所有现有记录。继承Collection<T>IEnumerable<T>

在我下面的代码中,我使用 Where 进行过滤方法,然后根据匹配计数执行逻辑。 Where似乎是这里的瓶颈。有比这更有效的方法吗?我绝不是 LINQ 专家。

foreach (var record in inputDataLines)
{
    var fields = record.Split(',');

    var fund = fields[0];
    var bps = Convert.ToDecimal(fields[1]);
    var withdrawalPct = Convert.ToDecimal(fields[2]);
    var percentile = Convert.ToInt32(fields[3]);
    var age = Convert.ToInt32(fields[4]);
    var bombOutTerm = Convert.ToDecimal(fields[5]);

    var matchingRows = existingRecords.Entities.Where(r => r["field_1"].ToString() == fund
                                      && Convert.ToDecimal(r["field_2"]) == bps
                                      && Convert.ToDecimal(r["field_3"]) == withdrawalPct
                                      && Convert.ToDecimal(r["field_4"]) == percentile
                                      && Convert.ToDecimal(r["field_5"]) == age);

    entitiesFound.AddRange(matchingRows);

    if (matchingRows.Count() == 0)
    {
        rowsToAdd.Add(record);
    }
    else if (matchingRows.Count() == 1)
    {
        if (Convert.ToDecimal(matchingRows.First()["field_6"]) != bombOutTerm)
        {
            rowsToUpdate.Add(record);
            entitiesToUpdate.Add(matchingRows.First());
        }
    }
    else
    {
        entitiesToDelete.AddRange(matchingRows);
        rowsToAdd.Add(record);
    }
}

编辑:我可以确认所有 existingRecords在执行此代码之前在内存中。上述循环中没有 IO 或 DB 访问。

最佳答案

Himbrombeere是的,您应该先执行查询并将结果放入集合中,然后再使用 Any , Count , AddRange或者任何方法将再次执行查询。在您的代码中,查询可能在每次循环迭代中执行 5 次。

注意文档中的术语延迟执行。如果一个方法以这种方式实现,则意味着该方法可用于构造 LINQ 查询(因此您可以将它与其他方法链接起来,最后您有一个查询)。但只有不使用延迟执行的方法,如 Count , Any , ToList (或普通的 foreach )将实际执行它。如果您不希望每次都执行整个查询并且您必须多次访问此查询,最好将结果存储在一个集合中(.f.e with ToList)。

但是,您可以使用一种效率更高的不同方法,Lookup<TKey, TValue>它类似于字典,可以使用匿名类型作为键:

var lookup = existingRecords.Entities.ToLookup(r => new 
{
    fund = r["field_1"].ToString(),
    bps = Convert.ToDecimal(r["field_2"]),
    withdrawalPct =  Convert.ToDecimal(r["field_3"]),
    percentile = Convert.ToDecimal(r["field_4"]),
    age = Convert.ToDecimal(r["field_5"])
});

现在您可以非常有效地在循环中访问此查找。

foreach (var record in inputDataLines)
{
    var fields = record.Split(',');
    var fund = fields[0];
    var bps = Convert.ToDecimal(fields[1]);
    var withdrawalPct = Convert.ToDecimal(fields[2]);
    var percentile = Convert.ToInt32(fields[3]);
    var age = Convert.ToInt32(fields[4]);
    var bombOutTerm = Convert.ToDecimal(fields[5]);

    var matchingRows = lookup[new {fund, bps, withdrawalPct, percentile, age}].ToList();

    entitiesFound.AddRange(matchingRows);

    if (matchingRows.Count() == 0)
    {
        rowsToAdd.Add(record);
    }
    else if (matchingRows.Count() == 1)
    {
        if (Convert.ToDecimal(matchingRows.First()["field_6"]) != bombOutTerm)
        {
            rowsToUpdate.Add(record);
            entitiesToUpdate.Add(matchingRows.First());
        }
    }
    else
    {
        entitiesToDelete.AddRange(matchingRows);
        rowsToAdd.Add(record);
    }
}

请注意,即使键不存在(返回空列表),这也会起作用。

关于c# - 搜索可枚举的最有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46788619/

相关文章:

linq - 将 Linq 表达式组合到 Func 时遇到问题

c# - 我是否误解了 LINQ to SQL .AsEnumerable()?

c# - 如何在不循环的情况下使用 System.TimeSpan 值实现模数运算?

c# - 在 LINQ 表达式中将 Int 转换为 String

C# 从选定的单元格中获取列标题文本和第一行文本

c# - 如何禁用 gif 图像的循环并将其保存在 C# 中

performance - PostgreSQL 的神奇之处在于系统驱动器上的可用空间不足

java - 内存中字节的大小 - Java

python - python 中类似 linq 的求和函数

c# - 使用字典进行重复数据删除的更好方法?