Linq检索在集合之间已更改的项目和不在集合中的项目

时间:2011-10-07 18:54:38

标签: c# linq linq-to-objects

以下是我的课程:

public class XDetail
{
    public string Name { get; set; }
    public int ID { get; set; }
}

public class X
{
    public int XID { get; set; }
    public int ID { get; set; }
}

它们之间共享ID以链接X和XDetail(一对多关系),而X和XDetail实际上是类型化的DataRows。我使用以下linq查询读入文件并形成匿名类型:

var results = (from line in File.ReadAllLines(file)
              select new
              {
                  XID = int.Parse(line.Substring(0, 8).TrimStart('0')),
                  Name = line.Substring(8, 255).Trim()
              }).ToList();

此数据用于检查现有的X / XDetail以进行适当的更改或添加新记录。我将结果包装成一个检查,以查看当序列没有结果时它是否会抛出.ToList()。 XList是List,XDetailList是List。

从那里我尝试一个花哨的linq查询来匹配相应的项目:

var changedData = from x in XList
                  join xDetail in XDetailList on x.ID equals xDetail.ID
                  where 
                  (!results.Any(p => p.XID.Equals(x.XID))
                  || !results.Any(p => p.Name.Equals(xDetail.Name)))                   
                  select new
                  {                       
                      XValue = x,
                      XDetailValue = xDetail,
                      Result = (from result in results
                               where result.Name.Equals(xDetail.Name)
                               select result).SingleOrDefault()
                  };

我的新问题是,此查询只会向我提供X / XDetail中已更改的内容,而不是新内容。为了获得新的东西,我必须运行另一个查询,这对于小数据集(X / XDetail的3个现有条目)进行测试时似乎已经足够好了,但是当我尝试真正的文件并且通过它来浏览它的~7700个条目我似乎有无尽的处理。

对于已包含在X / XDetail中的以下样本数据集:
    XID:1,姓名:Bob,ID:10
    XID:2,姓名:Joe,ID:20
    XID:3,姓名:Sam,ID:30

结果文件包含:
    XID:2,名称:Bob2
    XID:3,名称:NotSam
    XID:4,姓名:NewGuy
    XID:5,名称:NewGuy2

我希望能够获得包含以下内容的结果集:
    {XID:2,姓名:Bob2},x,xDetail
    {XID:3,姓名:NotSam},x,xDetail
    {XID:4,姓名:NewGuy},x,xDetail
    {XID:5,名称:NewGuy2},x,xDetail

我希望将x和xDetail作为结果集的一部分,以便我可以使用这些类型化的数据行进行必要的更改。

我试着提出这样的问题:

var newData = from result in results
              join x in XList on result.XID equals x.XID
              join xDetail in XDetailList on x.ID equals xDetail.ID
                      where
                      (x.XID == result.XID && xDetail.Name != result.Name)
                      select new
                      {
                          XValue = x,
                          XDetailValue = xDetail,
                          Result = result
                      };

由于联接表明我只是要获取数据中已更改的项目,我真的希望能够添加不在X / XDetail中的数据并停止我处理我的系统过去2.5小时内更改文件~7700。我觉得我已经盯着这个和相关的查询太长时间,无法发现我应该做什么来正确地为它塑造一个where子句。

有没有办法构建linq查询以查找已更改的数据和X / XDetail中不存在的数据并将其返回到要处理的新结果集中?

1 个答案:

答案 0 :(得分:2)

我认为您的效果问题与查询的复杂性有关,可能是O(n^2)左右。

因此,首先我建议您在查找结构中设置当前数据,如(*):

var joinedByXID = (from x in XList
                    join xDetail in XDetailList on x.ID equals xDetail.ID
                    select new { X = x, XDetail = xDetail })
                    .ToLookup(x => x.X.ID);

现在,我不确定,但我认为通过说“更改数据”你的意思是一个条目列表已经存在XID但新名称,是不是? 如果是这样,您可以使用此查询获取“已更改的数据”:

var changedData = results
.Where(r => joinedByXID.Contains(r.XID))
.SelectMany(r => joinedByXID[r.XID]
            .Where(x => x.XDetail.Name != r.Name)
            .Select(old => new {XValue=old.X, XDetailValue=old.XDetail, Result=r}));

然后,如果“新数据”是指具有新XID(XID当前不存在于XList / XDetailList中的XID)的条目列表,那么您无法将它们与X / Xdetail元素匹配,因为,没有,所以这很简单:

var newData = results
.Where(r => !joinedByXID.Contains(r.XID));

<强>(*)
实际上,为了更快,您可以将数据排列在字典字典中,其中外键是XI​​D,内键是名称。