Alexandru
Alexandru

Reputation: 12892

Concatenating LINQ query results when using loops

I need to do a loop over a bunch of files and spit out log entries through a LINQ query:

foreach (string file in Directory.EnumerateFiles(TextBoxLogDirectory.Text, "*.log"))
{
    FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
    using (LogReader reader = new LogReader(stream))
    {
        var events = (from x in reader.Parse().Where(y => y.IsInRange(range) && (y.EventNo == 1180 || y.EventNo == 1187) && y.GetDefaultMessageField().Contains(":Outbound/"))
                      group x by x.GetDefaultMessageField() into grouping
                      select new
                      {
                          ID = grouping.Key,
                          Event1180 = grouping.LastOrDefault(z => z.EventNo == 1180),
                          Event1187 = grouping.LastOrDefault(z => z.EventNo == 1187)
                      }).ToList();
    }
}

This query must run on a single file at a time, and the above works fine, but I need to keep appending the results of the query to an object outside of the foreach loop. Something like this (although this doesn't work, unfortunately):

dynamic events; // I want to append to this object outside of the loop's scope.
foreach (string file in Directory.EnumerateFiles(TextBoxLogDirectory.Text, "*.log"))
{
    FileStream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
    using (LogReader reader = new LogReader(stream))
    {
        events = (from x in reader.Parse().Where(y => y.IsInRange(range) && (y.EventNo == 1180 || y.EventNo == 1187) && y.GetDefaultMessageField().Contains(":Outbound/"))
                      group x by x.GetDefaultMessageField() into grouping
                      select new
                      {
                          ID = grouping.Key,
                          Event1180 = grouping.LastOrDefault(z => z.EventNo == 1180),
                          Event1187 = grouping.LastOrDefault(z => z.EventNo == 1187)
                      }).ToList().Concat(events);
    }
}

How can I achieve this sort of behavior?

Not sure if it helps, my query above will return objects with (string)ID, (LogEvent)Event1180, and (LogEvent)Event1187.

Upvotes: 0

Views: 711

Answers (1)

Vlad
Vlad

Reputation: 35584

You can try something like this:

Directory.EnumerateFiles(TextBoxLogDirectory.Text, "*.log")
         .SelectMany(ParseFile);

where ParseFile is defined as

IEnumerable<string> ParseFile(string file)
{
    using (FileStream stream =
               new FileStream(file, FileMode.Open, FileAccess.Read,
                              FileShare.ReadWrite | FileShare.Delete))
    using (LogReader reader = new LogReader(stream))
    {
        return (from x in reader.Parse()
                   .Where(y => y.IsInRange(range) &&
                          (y.EventNo == 1180 || y.EventNo == 1187) &&
                          y.GetDefaultMessageField().Contains(":Outbound/"))
                group x by x.GetDefaultMessageField() into grouping
                select new
                {
                    ID = grouping.Key,
                    Event1180 = grouping.LastOrDefault(z => z.EventNo == 1180),
                    Event1187 = grouping.LastOrDefault(z => z.EventNo == 1187)
                }).ToList();
    }
}

Or you can inline the helper function, if you want to.

This produces an IEnumerable<string>, which you perhaps should immediately materialize with ToList.


Edit: Oh, I see, we've got a type mismatch.

There are two ways of working around this: either you should name the anonymous type and make the function return IEnumerable<that type>, or just inline the function content into a lambda:

Directory.EnumerateFiles(TextBoxLogDirectory.Text, "*.log")
         .SelectMany(file =>
    {
        using (FileStream stream =
                   new FileStream(file, FileMode.Open, FileAccess.Read,
                                  FileShare.ReadWrite | FileShare.Delete))
        using (LogReader reader = new LogReader(stream))
        {
            return (from x in reader.Parse()
            // ...
                }).ToList();
        }
    }
);

Upvotes: 2

Related Questions