2009-12-11 97 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);