2012-07-24 58 views
3

我正在構建一個監視用戶計算機上的目錄的WPF應用程序。該應用程序從受監控的目錄中上傳文件,然後將一些信息保存到SQLite數據庫中。部分業務處理是不重新處理已上傳的文件,並重新上傳自上次上傳以來已上傳但已更改的文件。如何讓LINQ全外連接正常工作?

我有兩個幫助器方法,構建並返回一個List<FileMetaData>,我用LINQ - Full Outer Join加入。我的問題是,當我使用我的FileMetaData對象時,代碼似乎不起作用。似乎所有的東西都應該可以工作,但是我爲什麼不起作用而不知所措。我通常會嘗試作爲對其他主題的評論發佈,但我目前沒有「Rep」在這裏執行此操作。

下面是我構建的一個示例,顯示我的問題,如果您在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; 
    } 

編輯:

下面的答案正是我一直在尋找用的。我實現瞭如下定義和改變了我的呼籲var full = leftOuter.Union(rightOuter, new FileMetaDataCompare())IEqualityComparer ...

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

的問題是,Union會給你的結果消除重複通過檢查平等。當你使用匿名類型時,相等的定義是'所有字段都有相同的值'。當你聲明一個類型時,它將使用Equals方法。由於您未覆蓋Equals,因此它默認爲ReferenceEquals,這意味着無論其字段值如何,兩個單獨的實例都不相等。

三種方式來解決這個問題:聯盟後

1)使用匿名類型在查詢中,並轉換成定義類型:

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

2)定義的IEqualityComparer<FileMetaData>類定義要平等大公(只是文件路徑?所有字段?),並在FileMetaData通過它的一個實例來Union()

3)覆蓋Equals()(和GetHashCode())。

2)和3)將非常相似,但是覆蓋Equals()可以(並且將)在檢查平等時使用,而不僅僅是在這種情況下。

+0

非常感謝你的回答。我在IEqualityComparer上完全消隱了。 – Frito 2012-07-24 18:38:08