Я пытаюсь создать средство просмотра журналов, отображающее события из разных источников, но упорядоченное по метке времени. У меня есть желание использовать для этого C # Linq, но как?

Пример: у меня есть один список событий, считанных из файлов в список strig, отсортированный по отметке даты и времени.

Другой источник событий - вставки в базу данных, для которых я использую Linq для извлечения того же временного сегмента, что и первый список. В базе данных также есть отметка времени.

Я хочу иметь список, в котором перечислены все события, которые происходят в реальном времени. то есть вставка dabase может привести к исключению, зарегистрированному в файле на диске через секунду.

Думаю, я ищу способ объединить и отсортировать их в списки, разделяющие только одно общее поле - временную метку, и в итоге получится коллекция, которую я могу использовать с помощью foreach, даже если каждый элемент может быть разного типа.

Любые идеи ?

Мартин

2
Martin 11 Дек 2009 в 03:18

3 ответа

Лучший ответ

Вы можете использовать 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); 
1
Mark Byers 11 Дек 2009 в 11:40
Спасибо большое! Теперь мне просто нужно было выбрать немного здесь и там, чтобы заставить его работать :) Поскольку моим первым списком был List с меткой времени в s [0] Второй список был класса таблицы с одним элементом datetime. В итоге я получил: 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 (var m in ld) {if (m.Value is Nettbud) ... else ...} Мартин
 – 
Martin
11 Дек 2009 в 11:33
Спасибо, что сообщили нам, как у вас дела. И не забудьте принять ответ. :)
 – 
Mark Byers
11 Дек 2009 в 11:36
Извините, я здесь впервые. Не вижу никаких указаний по форматированию, расширению 600 символов и маркировке ответа! Мартин
 – 
Martin
11 Дек 2009 в 11:51
Добро пожаловать в StackOverflow! К сожалению, форматирование в комментариях невозможно - только в вопросах и ответах. Я добавил ваш код в конец своего ответа, чтобы он имел форматирование. Если вы хотите написать больше, чем уместно в комментарии, вы можете сделать новый «ответ» и сделать ссылку на него из комментария. Чтобы принять ответ, просто щелкните галочку рядом с ответом, чтобы он стал зеленым.
 – 
Mark Byers
11 Дек 2009 в 12:20

Я думаю, что приведенный ниже код должен достичь того, что вы ищете.

В основном вам просто нужно создать список объектов, а затем добавить свои списки журналов 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 является контейнерным классом, как в ответе @Mark Byers:

    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
11 Дек 2009 в 04:22

Объедините источники журналов, затем отсортируйте результаты. Предполагая, что каждый источник журнала представляет собой 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);
1
Thomas Levesque 11 Дек 2009 в 04:43