3

ユーザーのコンピューター上のディレクトリを監視する WPF アプリケーションを構築しています。アプリは、監視対象のディレクトリからファイルをアップロードし、一部の情報を SQLite データベースに保存します。ビジネス処理の一部は、既にアップロードされたファイルを再処理せず、アップロードされたが最後のアップロード以降に変更されたファイルを再アップロードすることです。

LINQList<FileMetaData>を使用してビルドして返すヘルパー メソッドが 2 つあります。完全外部結合を使用して結合します。私の問題は、オブジェクトを使用するとコードが機能しないように見えることです。すべてが機能するはずですが、なぜ機能しないのか途方に暮れています。私は通常、他のスレッドにコメントとして投稿しようとしますが、現在、それを行うための「担当者」がここにいません。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();
        }
    }
4

1 に答える 1

4

問題は、同等性をチェックして重複を排除するUnion結果が得られることです。匿名型を使用する場合、等価の定義は「すべてのフィールドが等しい値を持つ」です。型を宣言すると、メソッドが使用されます。をオーバーライドしていないため、デフォルトで になります。これは、フィールド値に関係なく、2 つの個別のインスタンスが等しくないことを意味します。 EqualsEqualsReferenceEquals

これを解決するには、次の 3 つの方法があります。

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()

Equals()3) で(およびGetHashCode()) をオーバーライドしFileMetaDataます。

2) と 3) は非常に似ていますが、オーバーライドEquals()は、この状況だけでなく、等しいかどうかをチェックするときにいつでも使用できます (また使用する予定です)。

于 2012-07-24T17:40:11.220 に答える