如何让这个LINQ全外连接正常运行?

我正在构建一个监视用户计算机上的目录的WPF应用程序。 该应用程序从受监控目录上传文件,然后将一些信息保存到SQLite数据库中。 业务处理的一部分是不重新处理已上载的文件,并重新上载已上载但自上次上载以来已更改的文件。

我有两个帮助方法,构建并返回一个List ,我使用LINQ – Full Outer Join来加入。 我的问题是,当我使用FileMetaData对象时,代码似乎不起作用。 看起来一切都应该都有效,但我不知道为什么它不起作用。 我通常会尝试在另一个post上发表评论,但我目前没有“Rep”来做这件事。

下面是我构建的示例,如果您在LINQpad中运行它, 则会显示我的问题。 在单击运行按钮之前,请确保将语言设置为“C#程序”。 我应该采取哪些不同的方式让样本与对象一起工作? 万分感谢!

  void Main() { var dbItems = new List() { new FileMetaData {FilePath = "C:\\Foo.txt", DbTimestamp = "1" }, new FileMetaData {FilePath = "C:\\FooBar.txt", DbTimestamp = "3" }, }; var fsItems = new List() { new FileMetaData {FilePath = "C:\\Bar.txt", FsTimestamp = "2" }, new FileMetaData {FilePath = "C:\\FooBar.txt", FsTimestamp = "3" }, }; var leftOuter = from d in dbItems join f in fsItems on d.FilePath equals f.FilePath into temp from o in temp.DefaultIfEmpty(new FileMetaData(){}) select new FileMetaData { FilePath = d.FilePath, DbTimestamp = d.DbTimestamp, FsTimestamp = o.FsTimestamp, }; var rightOuter = from f in fsItems join d in dbItems on f.FilePath equals d.FilePath into temp from o in temp.DefaultIfEmpty(new FileMetaData(){}) select new FileMetaData { FilePath = f.FilePath, DbTimestamp = o.DbTimestamp, FsTimestamp = f.FsTimestamp, }; var full = leftOuter.AsEnumerable().Union(rightOuter.AsEnumerable()); leftOuter.Dump("Left Results"); rightOuter.Dump("Right Results"); full.Dump("Full Results"); } // Define other methods and classes here public class FileMetaData { public string FilePath; public string DbTimestamp; public string FsTimestamp; } 

编辑:

以下答案正是我所寻找的。 我实现了如下定义的IEqualityComparer ,并将我的调用改为var full = leftOuter.Union(rightOuter, new FileMetaDataCompare())

  public class FileMetaDataCompare : IEqualityComparer { public bool Equals(FileMetaData x, FileMetaData y) { var areEqual = x.FilePath == y.FilePath; areEqual = areEqual && x.DbTimestamp == y.DbTimestamp; areEqual = areEqual && x.FsTimestamp == y.FsTimestamp; return areEqual; } public int GetHashCode(FileMetaData obj) { var hCode = string.Concat(obj.FilePath, obj.DbTimestamp, obj.FsTimestamp); return hCode.GetHashCode(); } } 

问题是Union会通过检查相等性来为您提供消除重复的结果。 当您使用匿名类型时,相等的定义是“所有字段都具有相等的值”。 声明类型时,它将使用Equals方法。 由于您没有重写Equals ,因此它默认为ReferenceEquals ,这意味着两个单独的实例无论其字段值如何都不相等。

解决这个问题的三种方法:

1)在查询中使用匿名类型并在Union之后转换为已定义的类型:

 var full = leftOuter.Union(rightOuter).Select( i=> new FileMetaData { FilePath = i.FilePath, DbTimestamp = i.DbTimestamp, FsTimestamp = i.FsTimestamp }); 

2)定义一个IEqualityComparer类,它定义你想要的相等性(只是FilePath?所有字段?)并将它的实例传递给Union()

3)在FileMetaData覆盖Equals() (和GetHashCode() )。

2)和3)将非常相似,但是当你检查相等时,重写Equals()可以(并且将会)使用,而不仅仅是在这种情况下。