设置列表执行linq查询结果会导致超时

本文关键字:结果 超时 查询 linq 列表 执行 设置 | 更新日期: 2023-09-27 18:07:07

我有一个扩展方法,它接受两个列表并比较它们以进行修改,然后输出一个新列表。下面是代码

public static List<Member> GetModifiedRecords(this List<Member> LocalMemberData, List<Member> RemoteMemberData)
    {
        var result = (from localdata in LocalMemberData
                      from remotedata in RemoteMemberData
                      where
                      ((
                      localdata.Card != remotedata.Card ||
                      localdata.DateJoined != remotedata.DateJoined ||
                      localdata.DatePaidUpTo != remotedata.DatePaidUpTo ||
                      localdata.Forename != remotedata.Forename ||
                      localdata.Postcode != remotedata.Postcode ||
                      localdata.State != remotedata.State ||
                      localdata.Street != remotedata.Street ||
                      localdata.Surname != remotedata.Surname ||
                      localdata.Title != remotedata.Title ||
                      localdata.Town != remotedata.Town
                      )
                      && (localdata.MemberNumber == remotedata.MemberNumber
                      ))
                      select localdata).Distinct();
        List<Member> modifiedMembers = new List<Member>(result);
        return modifiedMembers;
    } 

奇怪的是,当它在

行上运行时失败了
List<Member> modifiedMembers = new List<Member>(result);

带有错误

" CLR在60秒内无法从COM上下文0x3b4668转换到COM上下文0x3b44f8。拥有目标上下文/公寓的线程很可能正在执行非泵送等待,或者在没有泵送Windows消息的情况下处理长时间运行的操作。这种情况通常会对性能产生负面影响,甚至可能导致应用程序变得无响应或内存使用随着时间的推移不断累积。为了避免这个问题,所有单线程公寓(STA)线程都应该使用泵送等待原语(如CoWaitForMultipleHandles),并在长时间运行操作期间常规泵送消息。"

供参考,正在比较的两个列表都有超过100,000条记录。我想错了吗?

设置列表<T>执行linq查询结果会导致超时

如果我理解正确,您的LINQ查询将比较每个列表中的每个元素与另一个列表中的每个元素。这意味着100,000 x 100,000的比较。这是10亿次比较,非常多。

考虑一个连接,而不是过滤笛卡尔积。例如:

var r = new Random();
var list1 = Enumerable.Range(0,10000).OrderBy(_=>r.Next()).ToList();
var list2 = Enumerable.Range(0,10000).OrderBy(_=>r.Next()).ToList();
var sw = Stopwatch.StartNew();
var c1 = from x1 in list1 from x2 in list2 where x1==x2 select x1;
var j1 = c1.ToList();
sw.ElapsedMilliseconds.Dump();
sw=Stopwatch.StartNew();
var c2 = from x1 in list1 join x2 in list2 on x1 equals x2 select x1;
var j2 = c2.ToList();
sw.ElapsedMilliseconds.Dump();

给出

的计时
3584
1

。连接是超级快的(复杂度O(n)),你的伪连接不是(复杂度O(n2))。

在100,000项时,我的机器在笛卡尔产品过滤方法上卡住了(我在5分钟后退出),但在21ms内完成了连接。

所以,像下面这样重写你的查询应该真的会加速:

(from localdata in LocalMemberData
join remotedata in RemoteMemberData 
on localdata.MemberNumber equals remotedata.MemberNumber
where
(
localdata.Card != remotedata.Card ||
localdata.DateJoined != remotedata.DateJoined ||
localdata.DatePaidUpTo != remotedata.DatePaidUpTo ||
localdata.Forename != remotedata.Forename ||
localdata.Postcode != remotedata.Postcode ||
localdata.State != remotedata.State ||
localdata.Street != remotedata.Street ||
localdata.Surname != remotedata.Surname ||
localdata.Title != remotedata.Title ||
localdata.Town != remotedata.Town
)
select localdata).Distinct()

我不认为错误信息的内容是应该关注的地方。我认为你需要重新考虑你的比较操作,从可读性和执行的角度来简化和改进代码。

这是我如何阅读你的代码,你有2个集合:列表A和列表B。你想从列表A中返回存在于列表B中的项目,但不完全匹配每个属性。

也就是说,我已经编写了一个算法,它使用了一个稍微简单的'Member'模型,但演示了与您正在尝试的相同的功能。

public static List<Member> GetModifiedRecords(List<Member> localMemberData, List<Member> remoteMemberData )
    {
        var list = new List<Member>();
        foreach (var item in localMemberData)
        {
            var remoteItems = remoteMemberData.Where(q => q.Id == item.Id);
            if (remoteItems.Any())
            {
                var remoteItem = remoteItems.First();
                if (item.CompareTo(remoteItem) != 0) list.Add(item);
            }
        }
        return list;
    }
    public class Member : IComparable<Member>
    {
        public int Id { get; set; }
        public string Card { get; set; }
        public DateTime DateJoined { get; set; }
        public string PostalCode { get; set; }
        // TODO: add other properties
        public int CompareTo(Member other)
        {
            if (this.Card != other.Card) return 1;
            if (this.DateJoined != other.DateJoined) return 1;
            if (this.PostalCode != other.PostalCode) return 1;
            // TODO: add other properties
            return 0;
        }
    }