views:

534

answers:

2

Hello all,

Definitely a LINQ newbie, but very experienced with SQL and C# and wondering if this is possible in LINQ. If so, I could use it other places, but I figured this would be a good starting point (and help to simplify/clean up some code). This could be more generalized, but I figured this might be a good real-life example that could help explain.

Quick background: I'm doing a personal learning project building a scheduler and learning Spring.NET/DI, Fluent NHibernate, Quartz.NET, and trying to get my feat wet with TDD. Learned a ton so far.

A Quartz.NET IScheduler object has these property(1)/methods(2) (assume public)...

string[] JobGroupNames { get; }
string[] GetJobNames(string groupName)
Trigger[] GetTriggersOfJob(string jobName, string groupName)

Assume Trigger definition is just...

class Trigger
{  
    string Name { get; }
}

I have a class I'm trying to get a list of which has a constructor like the following (because it's immutable once created)...

class QuartzJob
{
    public QuartzJob(Guid groupId, Guid jobId, IEnumerable<string> triggerNames)
}

Currently this is how I'm handling it...

public IEnumerable<QuartzJob> GetQuartzInfo(IScheduler scheduler)
{
    List<QuartzJob> list = new List<QuartzJob>();

    foreach (string grp in scheduler.JobGroupNames)
    {
        foreach (string job in scheduler.GetJobNames(grp))
        {
            var triggerNames = scheduler
                .GetTriggersOfJob(job, grp)
                .ToList()
                .ConvertAll(t => t.Name);

            var qj = new QuartzJob(new Guid(grp), new Guid(job), triggerNames);
            list.Add(qj);
        }
    }    
    return list;
}

This way works fine (albeit maybe a little slow and complex), but those double foreach loops bug me and since I'm a "learn LINQ" kick, I thought this would a good chance and attempt to apply it.

Not asking for someone to write the code for me, as this is a learning project (although you are more then welcome to), just trying to see if LINQ can do things like this, and if so, looking for some more information on it... Method calls with query values, and use those values to build another query. If so, it would reduce some of my multiple foreach loops I have in other places in my code.

Thanks!

+2  A: 

The key to doing this in LINQ is to understand that .Select is your friend--but a surly friend who might hit you. I kid. You can use Select and it's cousin SelectMany to transform your arrays on the fly.

public IEnumerable<QuartzJob> GetQuartzInfo(IScheduler scheduler)
{
    IEnumerable<QuartzJob> jobs = scheduler.JobGroupNames.SelectMany(   // Using SelectMany because there is an IEnumerable<QuartzJob> for each group and we want to flatten that.
        groupName => scheduler.GetJobNames(groupName).Select(  // Returns an IEnumerable<QuartzJob> for each group name found.
            jobName => 
                // We're doing a lot in this new but essentially it creates a new QuartzJob for each jobName/groupName combo
                new QuartzJob(new Guid(groupName), new Guid(jobName),
                    scheduler.GetTriggersOfJob(jobName, groupName).Select(trigger => trigger.Name)  // This transforms the GetTriggersOfJob into an IEnumerable<string> for use in the constructor of QuartzJob
                    ))); 
    return new List<QuartzJob>(jobs);
}

Or, if you prefer the inline query language it'd be a bit more readable and look like this:

public IEnumerable<QuartzJob> GetQuartzInfo(IScheduler scheduler)
{
    IEnumerable<QuartzJob> jobs = from groupName in scheduler.JobGroupNames
                                  from jobName in scheduler.GetJobNames(groupName) // stacking the two froms is the equivalent of SelectMany because the first select is defaulted as the result of the second.
                                  select new QuartzJob(new Guid(groupName), new Guid(jobName),
                                      // this sub-select is to get just the IEnumerable<string> of trigger names needed for the constructor.
                                      (from trigger in scheduler.GetTriggersOfJob(jobName, groupName)
                                       select trigger.Name));
    return new List<QuartzJob>(jobs);
}
Jacob Proffitt
That is exactly what I am looking for. You are my hero! Do you care if I credit you and link back to this post in my project? (so I can keep track where I learned this stuff)The project is at http://code.google.com/p/dotnetjobscheduler/
Steve Foster
Not at all. Tell 'em all to vote for my answer :).
Jacob Proffitt
A: 

Hi Steve, I would really like to check your your dotnetjobscheduler, but the site seems to be locked, or down. Can you confirm that is at at google code, or is there another place I can view it? Cheers

Magick
It was moved to http://code.google.com/p/jezel/Language switched to java because of Mono/Linux issues.The last source I had in DotNet is published in the download's section.
Steve Foster