查询列表最有效(也是最快)的方法

Most efficient (& fastest) way to query a list

我正在尝试找出查询列表的最高效方法。我知道那里有很多例子,而且之前已经出现过很多,但我对此很陌生,我正在努力研究如何将一些概念应用到我的情况中。

private static void KeepMatchesBasedOnRestrictions(ref List<Entity> matches, 
        List<Entity> preFilteredShifts, List<Entity> locationalInformations)
    {
        if (matches.Count == 0) return;

        matches.RemoveAll(

            (match) => ( GeographyHasRestriction(match, preFilteredShifts, locationalInformations) )

            );
    }

private static bool GeographyHasRestriction(Entity match, List<Entity> preFilteredShifts, List<Entity> locationalInformations)
    {                  
        EntityReference fw = match.GetAttributeValue<EntityReference>("crm_fw");

        Entity shift = preFilteredShifts.Single<Entity>( 
                a => match.GetAttributeValue<EntityReference>("crm_shift").Id == a.Id
            );
        EntityReference trust = shift.GetAttributeValue<EntityReference>("crm_trust");
        EntityReference location = shift.GetAttributeValue<EntityReference>("crm_location");
        EntityReference ward = shift.GetAttributeValue<EntityReference>("crm_ward");

        Dictionary<Guid, Entity> locInfoRecs = locationalInformations.ToDictionary(p => p.Id);

        var locationalInformationQuery = from loc in locationalInformations
                                         where (
                                            (
                                                loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                                && !loc.Contains("crm_trust")
                                                && !loc.Contains("crm_location")
                                                && !loc.Contains("crm_ward")
                                            )
                                            ||
                                            (
                                                loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                                && !loc.Contains("crm_location")
                                                && !loc.Contains("crm_ward")
                                            )
                                            ||
                                            (
                                                loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_location").Id == location.Id
                                                && !loc.Contains("crm_ward")
                                            )
                                            ||
                                            (
                                                loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_location").Id == location.Id
                                                && loc.GetAttributeValue<EntityReference>("crm_ward").Id == ward.Id
                                            )
                                         )
                                         select loc;

        foreach (Entity loc in locationalInformationQuery)
        {
            if (loc.GetAttributeValue<bool>("crm_hasrestriction"))
            {
                return true;
            }
        }

        //return false;
    }

所以我认为我的问题是双重的;

  1. locationalInformationQuery 查询似乎 运行 非常慢......我说的是每次迭代最多 2 秒的内容,这太可怕了。
  2. 我还怀疑由于列表的性能问题,调用 matches.RemoveAll() 的方法也存在一些缺陷。

所以就解决这个问题而言,我认为我可以通过将我的 locationalInformations list 转换为其他类型的容器(例如 Dictionary 来获得更好的性能, HashSetSortedList。我的问题是我不知道如何调整我的查询以利用那些更高效的容器。

就第二点而言,我也很想知道使用 List.RemoveAll() 的替代方案。我可以在合理的范围内灵活地修改我的传入容器类型,这可能是可行的。

关于列表大小以防万一,匹配包含几千个项目,preFilteredShiftslocationalInformations 每个包含 > 100,000 个项目。

顺便说一句,我试过使用 Parallel.ForEach 而不是 foreach,但它几乎没有任何区别。

编辑:只是为了澄清一些问题,我在记忆中做这一切。我已经完全填充了我的所有列表,因此不应该有任何额外的数据库往返。我相当确定 GetAttributeValue<EntityReference> 不会初始进一步的数据库开销。

此外,是的,这是调用 Dynamics CRM Online 的本地应用程序。

代码 -

foreach (Entity loc in locationalInformationQuery)
    {
        if (loc.GetAttributeValue<bool>("crm_hasrestriction"))
        {
            return true;
        }
    }

可能是速度缓慢的原因之一。您正在获取更多数据,然后在内存中枚举它们。您可以在获取之前直接执行检查,这样您将获取更少的数据并且速度更快。像这样 -

return (from loc in locationalInformations
                                     where ((
                                        (
                                            loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                            && !loc.Contains("crm_trust")
                                            && !loc.Contains("crm_location")
                                            && !loc.Contains("crm_ward")
                                        )
                                        ||
                                        (
                                            loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                            && !loc.Contains("crm_location")
                                            && !loc.Contains("crm_ward")
                                        )
                                        ||
                                        (
                                            loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_location").Id == location.Id
                                            && !loc.Contains("crm_ward")
                                        )
                                        ||
                                        (
                                            loc.GetAttributeValue<EntityReference>("crm_fw").Id == fw.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_trust").Id == trust.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_location").Id == location.Id
                                            && loc.GetAttributeValue<EntityReference>("crm_ward").Id == ward.Id
                                        )
                                     ) && loc.GetAttributeValue<bool>("crm_hasrestriction")) // do the check before fetch in here
                                     select loc).Any(); 

我有时发现,当您使用的查询非常复杂时,查询 CRM 数据库可能会导致查询效率低下。

有时这可能是由于查询的生成方式取决于您查询数据库的方法,或者有时可能是迭代某些 IEnumerable 类型集合和检查条件会导致许多 SQL 查询每次迭代的数据库。或许可以使用 SQL Profiler 检查您的数据库背后发生的事情。结果可能是有见地的。

有时我觉得 CRM query limitations 太过妨碍性能,我已经恢复到的一个选项是回退到直接 ADO.NET 和 SQL 反对过滤视图我可以访问查询计划并且对正在发生的事情有更好的想法和理解。我敢肯定,许多 CRM 纯粹主义者现在对我皱眉,但我认为就最终用户体验而言,这是一个公平的说法,同时也使您的代码也相对容易理解。复杂的查询在代码中可能非常笨拙,拥有一个可以参考的 SQL 查询可以极大地帮助理解您的解决方案。您还可以从基于集合的操作和更少的 "chatty" 接口中获益,从而减少结果数据库调用的数量。

在您上面的问题中,如果您认为这可能是一个不错的选择,我会考虑通过提供类似 ;

的方法来制作此类解决方案的原型
private static bool GeographyHasRestrictionBySql(Entity match, List<Entity> preFilteredShifts, List<Entity> locationalInformations)
{
     // Query here, and determine your boolean result to return
}

这样您就可以通过更改调用函数来简单快速地测试它。