如何我得到这个LINQ全外连接才能正常工作? [英] How to I get this LINQ full-outer-join to function properly?

查看:181
本文介绍了如何我得到这个LINQ全外连接才能正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我有一个建立并返回一个名单,其中两个辅助方法; FileMetaData> ,我用的 LINQ - 全外连接加入。我的问题是,code似乎没有,当我用我的 FileMetaData 对象的工作。好像一切都应该工作,但我很茫然,为什么它不工作。我通常会尝试发布的其他线程上的评论,但我目前还没有的代表在这里做到这一点。

下面是一个示例我已经建立,显示我的问题,如果您在 LINQpad 运行它。请确保您设置语言为C#程序你点击运行按钮之前。我应该怎么做不同有与对象样品的工作?由于一吨!

 无效的主要()
    {
        VAR dbItems =新的名单,其中,FileMetaData>(){
                新FileMetaData {文件路径=C:\\ foo.txt的,DBTIMESTAMP =1},
                新FileMetaData {文件路径=C:\\ FooBar.txt,DBTIMESTAMP =3},
            };

        VAR fsItems =新的名单,其中,FileMetaData>(){
                新FileMetaData {文件路径=C:\\跳回到bar.txt,FsTimestamp =2},
                新FileMetaData {文件路径=C:\\ FooBar.txt,FsTimestamp =3},
            };

            VAR leftOuter =从D在dbItems
                    加入f由于fsItems上d.FilePath等于f.FilePath
                    成温
                    从o在temp.DefaultIfEmpty(新FileMetaData(){})
                    选择新FileMetaData {
                        文件路径= d.FilePath,
                        DBTIMESTAMP = d.DbTimestamp,
                        FsTimestamp = o.FsTimestamp,
                    };

            VAR rightOuter =从f由于fsItems
                    加入d在dbItems上f.FilePath等于d.FilePath
                    成温
                    从o在temp.DefaultIfEmpty(新FileMetaData(){})
                    选择新FileMetaData {
                        文件路径= f.FilePath,
                        DBTIMESTAMP = o.DbTimestamp,
                        FsTimestamp = f.FsTimestamp,
                    };

            。VAR全= leftOuter.AsEnumerable()联盟(rightOuter.AsEnumerable());

            leftOuter.Dump(左结果);
            rightOuter.Dump(正确的结果);

            full.Dump(全部结果);
    }

    //定义其他的方法和类在这里
    公共类FileMetaData
    {
        公共字符串文件路径;
        公共字符串DBTIMESTAMP;
        公共字符串FsTimestamp;
    }
 

编辑:

下面的答案正是我一直在寻找。我实现了的IEqualityComparer 定义如下,改变了我的电话,以 VAR全= leftOuter.Union(rightOuter,新FileMetaDataCompare()) ...

 公共类FileMetaDataCompare:的IEqualityComparer< FileMetaData>
    {
        公共布尔等于(FileMetaData X,FileMetaData Y)
        {
            VAR areEqual = x.FilePath == y.FilePath;
            areEqual = areEqual和放大器;&安培; x.DbTimestamp == y.DbTimestamp;
            areEqual = areEqual和放大器;&安培; x.FsTimestamp == y.FsTimestamp;

            返回areEqual;
        }

        公众诠释GetHash code(FileMetaData OBJ)
        {
            变种H code = string.Concat(obj.FilePath,obj.DbTimestamp,obj.FsTimestamp);
            返回H code.GetHash code();
        }
    }
 

解决方案

问题是,联盟会给你结果的消除重复的用检查是否相等。当您使用匿名类型,平等的定义是各个领域都有平等的价值。当你声明一个类型,它会使用等于方法。既然你还没有覆盖等于,则默认为的ReferenceEquals ,这意味着两个分开的情况下,是不相等的,不论其字段值。

三种方式来解决这个问题:

1)使用匿名类型的查询和联盟后转换为定义类型:

  VAR全= leftOuter.Union(rightOuter)。选择(
    I =>新FileMetaData {
        文件路径= i.FilePath,
        DBTIMESTAMP = i.DbTimestamp,
        FsTimestamp = i.FsTimestamp
    });
 

2)定义的的IEqualityComparer&LT; FileMetaData&GT; <?/ code>类,定义你想(只是文件路径各领域的平等大公),并通过它的一个实例联盟()

3)覆盖等于()(和 GetHash code())的 FileMetaData

2),3)将是非常相似的,但覆盖等于()可以(会),只要您检查平等,而不仅仅是在这种情况下使用。

I am building a WPF application that monitors a directory on the users computer. The app uploads files from the monitored directory and then saves off some information into a SQLite db. Part of the business processing is to not re-process files that have already been uploaded and to re-upload files that have been uploaded but have changed since the last upload.

I have two helper methods that build and return a List<FileMetaData> that I used LINQ - Full Outer Join to join the. My problem is that the code doesn't seem to work when I use my FileMetaData object. It seems like everything should all work but I'm at a loss as to why it's not working. I would normally try to post as a comment on the other thread but I don't currently have the "Rep" here to do that.

Below is a sample I've built that shows my problem if you run it in LINQpad. Make sure you set the language as "C# Program" before you click the run button. What should I do differently to have the sample work with the objects? Thanks a ton!

    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;
    }

EDIT:

The answer below was exactly what I was looking for. I implemented the IEqualityComparer as defined below and changed my call to 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();
        }
    }

解决方案

The problem is that Union will give you the results eliminating duplicates by checking for equality. When you use anonymous types, the definition of equality is 'all fields have an equal value'. when you declare a type, it will use the Equals method. Since you have not overridden Equals, it defaults to ReferenceEquals, which means that two separate instances are not equal regardless of their field values.

Three ways to solve this:

1) Use anonymous types in your queries and convert to defined types after Union:

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

2) Define an IEqualityComparer<FileMetaData> class that defines equality thay you want to (just FilePath? all fields?) and pass an instance of it to Union()

3) Override Equals() (and GetHashCode()) in FileMetaData.

2) and 3) will be very similar, but overriding Equals() can (and will) be used whenever you check for equality, not just in this situation.

这篇关于如何我得到这个LINQ全外连接才能正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆