2009-12-11 42 views
3

我想使日誌查看器顯示來自不同來源的事件,但按時間戳排序。我有一個感覺,我可以使用C#Linq爲此,但如何?Linq排序不同種類的列表

示例:我有一個從文件中讀取事件的列表,並將其列入按日期時間戳排序的條帶列表中。

事件的另一個來源是數據庫插入,我使用Linq來提取與第一個列表相同的時間段。該數據庫也有一個時間戳。

我想要的是列表顯示實時發生的所有事件。即一個dabase插入可能會導致一秒鐘後在磁盤文件中記錄異常。

我想我正在尋找一種方法來加入和排序這些列表共享只有一個公共字段的時間戳列表,結束了我可以使用foreach的集合,即使每個元素可能是不同的種類。

什麼想法?

Martin

回答

1

您可以使用Linq將兩個數據源轉換爲相同類型,然後將它們合併並對它們進行排序。在這裏,我有一個來自假裝數據庫表T_Log的對象,它具有Timestamp字段和其他一些字段,另一個來源是一些來自假文件的字符串,其中每個字符串包含行開頭的時間戳。我將它們轉換爲自定義類CommonLog,然後用它來排序。 CommonLog包含對原始對象的引用,所以如果我需要更詳細的信息,我可以投射並獲取該信息。

更輕量級的實現可以轉換爲已存在的類,如KeyValuePair<DateTime, object>

下面的代碼:

using System; 
using System.Collections.Generic; 
using System.Linq; 

class Program 
{  
    // Fake database class. 
    class T_Log 
    { 
     public DateTime Timestamp { get; set; } 
     public string Info { get; set; } 
     public int Priority { get; set; } 
    } 

    static void Main(string[] args) 
    { 
     // Create some events in the fake database. 
     List<T_Log> dbLogs = new List<T_Log> { 
      new T_Log { Timestamp = new DateTime(2009, 2, 5), Info = "db: foo", Priority = 1 }, 
      new T_Log { Timestamp = new DateTime(2009, 2, 9), Info = "db: bar", Priority = 2 } 
     }; 

     // Create some lines in a fake file. 
     List<string> fileLogs = new List<string> { 
      "2009-02-06: File foo", 
      "2009-02-10: File bar" 
     }; 


     var logFromDb = 
      dbLogs.Select(x => new CommonLog(
          x.Timestamp, 
          string.Format("{1} [Priority={2}]", 
             x.Timestamp, 
             x.Info, 
             x.Priority), 
          x)); 

     var logFromFile = 
      fileLogs.Select(x => new CommonLog(
          DateTime.Parse(x.Substring(0, x.IndexOf(':'))), 
          x.Substring(x.IndexOf(':') + 2), 
          x 
       )); 

     var combinedLog = logFromDb.Concat(logFromFile).OrderBy(x => x.Timestamp); 
     foreach (var logEntry in combinedLog) 
      Console.WriteLine("{0}: {1}", logEntry.Timestamp, logEntry.Log); 
    } 
} 

// This class is used to store logs from any source. 
class CommonLog 
{ 
    public CommonLog(DateTime timestamp, 
        string log, 
        object original) 
    { 
     this.Timestamp = timestamp; 
     this.Log = log; 
     this.Original = original; 
    } 

    public DateTime Timestamp { get; private set; } 
    public string Log { get; private set; } 
    public object Original { get; private set; } 
} 

輸出:

05-02-2009 00:00:00: db: foo [Priority=0] 
06-02-2009 00:00:00: file: baz 
09-02-2009 00:00:00: db: bar [Priority=0] 
10-02-2009 00:00:00: file: quux 

更新:馬丁回答說在這條信息的評論下面,但由於缺乏在評論格式是很難讀。這裏是格式化:

var ld = rs.Select(x => new KeyValuePair<DateTime, object>(DateTime.Parse(x[0]), x)) 
      .Concat(ta.Select(y => new KeyValuePair<DateTime, object>(y.Tidspunkt, y))) 
      .OrderBy(d => d.Key); 
+0

非常感謝很多人!現在,我不得不在這裏挑一點,那裏得到它的工作:) 作爲我的第一個名單是名單與S中的時間戳[0] 第二個列表是一個表類的一個元素的日期時間 結合所有我結束了: var ld = rs.Select(x => new KeyValuePair (DateTime.Parse(x [0]),x)) .Concat(ta.Select(y => new KeyValuePair (y.Tidspunkt,y)))。OrderBy(d => d.Key); 而作爲值類型是多態的: 的foreach(在LD VAR米) { 如果(m.Value是Nettbud) ... 其他 ... } 馬丁 – Martin 2009-12-11 08:33:04

+0

感謝您讓我們知道你怎麼了。並且不要忘記接受答案。 :) – 2009-12-11 08:36:58

+0

抱歉,這是我第一次來這裏。看不到任何格式指南,擴大600chars和標記回答! Martin – Martin 2009-12-11 08:51:46

1

我認爲下面的代碼應該可以達到您的要求。

基本上你只需要創建一個對象列表,然後將db &文件日誌的列表添加到該列表。一旦這樣做了,你可以寫一個委託來處理分揀

List<Db> db_inserts = new List<Db>(); 
    // populate list of db events here 

    List<Fi> files = new List<Fi>(); 
    // populate list of file events here 

    List<object> all = new List<object>(); 
    all.AddRange(db_inserts.Cast<object>()); 
    all.AddRange(files.Cast<object>()); 

    // sort the list by time 
    all.Sort(delegate(object a, object b) 
    { 
     DateTime aTime = DateTime.MinValue; 
     DateTime bTime = DateTime.MinValue; 

     if (a is Db) 
      aTime = ((Db)a).time; 
     else if (a is Fi) 
      aTime = ((Fi)a).time; 

     if (b is Db) 
      bTime = ((Db)b).time; 
     else if (b is Fi) 
      bTime = ((Fi)b).time; 

     return aTime.CompareTo(bTime); 
    }); 

編輯:上面的代碼可以使用下面的代碼(假設LogItem是一個容器類像@馬克拜爾斯得到改善回覆:

List<LogItem> all = new List<LogItem>(); 
    all.AddRange(db_inserts.Select(x => new LogItem { time = x.time, msg = x.name, source=x})); 
    all.AddRange(files.Select(x => new LogItem{time = x.time, msg = x.name, source = x})); 

    var query = all.OrderBy(x => x.time); 
1

連接日誌源,然後對結果進行排序。假設每個日誌源是IEnumerable<LogEntry>

var logSources = new [] 
{ 
    GetFileLogs(), 
    GetDbLogs() 
    // whatever other sources you need... 
}; 

var entries = Enumerable.Empty<LogEntry>(); 
foreach (var source in logSources) 
{ 
    entries = entries.Concat(source); 
} 

entries = entries.OrderBy(e => e.Timestamp);