0

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.

1 Answer 1

2

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();
        }
    }
);
Sign up to request clarification or add additional context in comments.

5 Comments

This doesn't work. It complains about the "select" statement which is part of the "select new" with the following error: "Error 2 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion exists (are you missing a cast?)"
@Alexandru: One more important correction: added ToList into the parser function, as it's closing the reader!
Yes, I see now...all I needed to do was actually create a class with three public methods...one string, one LogEvent, and another LogEvent, whose names must match that of my query, and I can "select new <ClassTypeName>"
@Alexandru: yes, but you can avoid it if you inline the function as at the bottom of the answer.
Interesting. See, there's a lot of things about LINQ I never knew and learn every day. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.