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

标签 c# .net linq full-outer-join

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

我有两个构建和返回 List<FileMetaData> 的辅助方法我用过LINQ - Full Outer Join加入。我的问题是当我使用我的 FileMetaData 时代码似乎不起作用目的。似乎一切都应该正常,但我不知道为什么它不起作用。我通常会尝试在另一个线程上发表评论,但我目前没有“代表”可以这样做。

下面是我构建的示例,如果您在 LINQpad 中运行它,它会显示我的问题.在单击运行按钮之前,请确保将语言设置为“C# 程序”。我应该做些什么不同的事情来让样本与对象一起工作?非常感谢!

    void Main()
    {
        var dbItems = new List<FileMetaData>() { 
                new FileMetaData {FilePath = "C:\\Foo.txt", DbTimestamp = "1" },
                new FileMetaData {FilePath = "C:\\FooBar.txt", DbTimestamp = "3" },
            };

        var fsItems = new List<FileMetaData>() {
                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<FileMetaData>
    {
        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<FileMetaData>定义你想要的相等性的类(只是 FilePath?所有字段?)并将它的一个实例传递给 Union()

3) 覆盖 Equals() (和 GetHashCode() )在 FileMetaData 中.

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

关于c# - 如何让这个 LINQ 全外连接正常运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11635568/

相关文章:

c# - 将数据客户端发布到服务器 C# 的最佳方式

c# - 返回泛型类型的实例

performance - 多重连接 LINQ 查询性能

c# - 如何确保用户只被添加到列表一次

c# - 代码从 C++ 转换为 C# 的问题

c# - "Path.GetFullPath"和网络路径

c# - 无法读取 Excel 文件 - File.open 无效

c# 删除重复算法 LINQ

c# - 类型不可知类在泛型类中调用泛型和非泛型方法?

c# - 从委托(delegate)中获取属性名称 i => i.Name