Click or drag to resize

LINQ Queries on Code

Examples of using LINQ queries on a code object tree.

LINQ queries on code objects in a particular local scope
C#
// LINQ queries can be performed on any collection, and therefore on the immediate child code objects at a
// particular point in a code tree using any of the various child object collections.  Examples are shown
// here both in SQL syntax and also the code alternative.  Global queries are shown in the next example.

Solution solution = Solution.Load("Nova.Examples.sln");
if (solution != null)
{
    // Use LINQ to find project(s) in a solution
    var examplesProject = (from project in solution.Projects
    where project.Name == "Nova.Examples"
    select project).FirstOrDefault();
    var examplesProject2 = solution.Projects.FirstOrDefault(project => project.Name == "Nova.Examples");
    if (examplesProject != null)
    {
        Log.WriteLine("Found " + examplesProject);

        // Use LINQ to find CodeUnit(s) in a project
        var programFile = (from codeUnit in examplesProject.CodeUnits
        where codeUnit.Name == "Program.cs"
        select codeUnit).FirstOrDefault();
        var programFile2 = examplesProject.CodeUnits.FirstOrDefault(codeUnit => codeUnit.Name == "Program.cs");
        if (programFile != null)
        {
            Log.WriteLine("Found " + programFile);

            // Use LINQ to find ClassDecl(s) in a CodeUnit.  Note that TypeDecls in CodeUnits can be nested
            // inside one or more NamespaceDecls - the GetAllTypeDecls() method will enumerate all of them.
            // You can also use 'project.Find()' or '@namespace.Find()' as shown in the FindCodeObjects() example.
            var programClass = (from classDecl in programFile.GetAllTypeDecls().OfType<ClassDecl>()
                                where classDecl.Name == "Program"
                                select classDecl).FirstOrDefault();
            var programClass2 = programFile.GetAllTypeDecls().OfType<ClassDecl>().FirstOrDefault(classDecl => classDecl.Name == "Program");
            if (programClass != null)
            {
                Log.WriteLine("Found " + programClass);

                // Use LINQ to find all static MethodDecls in a ClassDecl
                var methods = from methodDecl in programClass.Body.OfType<MethodDecl>()
                              where methodDecl.IsStatic
                              select methodDecl;
                var methods2 = programClass.Body.OfType<MethodDecl>().Where(methodDecl => methodDecl.IsStatic);
                Log.WriteLine("Found " + methods.Count() + " static MethodDecls in " + programClass);

                var methodMain = methods.FirstOrDefault(method => method.Name == "Main");
                if (methodMain != null)
                {
                    // Use LINQ to find all top-level Statements in a MethodDecl
                    var statements = from statement in methodMain.Body.OfType<Statement>()
                                     select statement;
                    var statements2 = methodMain.Body.OfType<Statement>();
                    Log.WriteLine("Found " + statements.Count() + " top-level Statements in " + methodMain);
                }
            }
        }
    }
}
LINQ queries on code objects at a global scope
C#
// LINQ queries can be performed across nested collections by chaining 'from' clauses and/or with the use of
// custom enumerators which recursively traverse trees of collections.  Such techniques make it easy to query
// code as if it were in a database, searching for patterns of code or calculating advanced code metrics.
// The examples shown here simply select all objects of a particular type, but various criteria can be applied
// (such as with a 'where' clause) to select whatever sub-set of the objects is desired.

Solution solution = Solution.Load("Nova.Examples.sln");
if (solution != null)
{
    // Query all files (CodeUnits) in an entire Solution
    var allFiles = from project in solution.Projects
                   from codeUnit in project.CodeUnits
                   select codeUnit;
    Log.WriteLine("Found " + allFiles.Count() + " total files in the entire Solution");

    // Query all Namespaces in an entire Solution (declared or imported from referenced assemblies)
    var allNamespaces = from project in solution.Projects
                        from @namespace in project.GlobalNamespace.GetAllChildren<Namespace>()
                        select @namespace;
    Log.WriteLine("Found " + allNamespaces.Count() + " Namespaces (declared or imported) in the Solution");

    // Note that each Project maintains its own Namespaces because the Types and TypeDecls in the Namespaces depend
    // upon the referenced assemblies and Projects, which can be different for each Project.  So, querying at the
    // Solution level can result in duplicate Namespaces and/or Types when multiple Projects exist, athough TypeDecls
    // will generally not be duplicated (but could be if the same source file is included in more than one Project).

    // Query all types in an entire Solution (imported from referenced assemblies).
    // Varies if using Mono Cecil vs Reflection vs either of them.
    var allTypes = from project in solution.Projects
                   from type in project.GlobalNamespace.GetAllChildren<object>()  // Or TypeDefinition or Type
                   where type is TypeDefinition || type is Type  // Omit if using TypeDefinition or Type above
                   select type;
    Log.WriteLine("Found " + allTypes.Count() + " imported Types in the Solution");

    // Query all TypeDecls in an entire Solution
    var allTypeDecls = from project in solution.Projects
                       from typeDecl in project.GlobalNamespace.GetAllChildren<TypeDecl>()
                       select typeDecl;
    Log.WriteLine("Found " + allTypeDecls.Count() + " TypeDecls in the Solution");

    Project exampleProject = solution.FindProject("Nova.Examples");
    if (exampleProject != null)
    {
        // Query all Namespaces in a Project (declared or imported from referenced assemblies and Projects)
        var namespaces = from @namespace in exampleProject.GlobalNamespace.GetAllChildren<Namespace>()
                         select @namespace;
        Log.WriteLine("Found " + namespaces.Count() + " Namespaces (declared or imported) in the Project");

        // Query all Types in a Project (imported from referenced assemblies).
        // Varies if using Mono Cecil vs Reflection vs either of them.
        var types = from type in exampleProject.GlobalNamespace.GetAllChildren<object>()  // Or TypeDefinition or Type
                    where type is TypeDefinition || type is Type  // Omit if using TypeDefinition or Type above
                    select type;
        Log.WriteLine("Found " + types.Count() + " imported Types in the Project");

        // Query all TypeDecls in a Project (declared or imported from referenced Projects)
        var typeDecls = from typeDecl in exampleProject.GlobalNamespace.GetAllChildren<TypeDecl>()
                        select typeDecl;
        Log.WriteLine("Found " + typeDecls.Count() + " TypeDecls (declared or imported) in the Project");

        // Query all TypeDecls that are declared in a Project (excluding those imported from other Projects)
        var declaredTypeDecls = from typeDecl in exampleProject.GetAllDeclaredTypeDecls()
                                select typeDecl;
        Log.WriteLine("Found " + declaredTypeDecls.Count() + " TypeDecls declared in the Project");
    }

    var timer = new Stopwatch();

    // Query ALL CodeObjects in an entire Solution
    timer.Start();
    var codeObjects = from codeObject in solution.GetAllChildren<CodeObject>()
                      select codeObject;
    double elapsed = timer.Elapsed.TotalSeconds;
    Log.WriteLine("Found " + codeObjects.Count() + " CodeObjects in the Solution, elapsed time: " + elapsed.ToString("N6"));

    // Query with parallel processing (PLINQ) for improved performance
    timer.Restart();
    var codeObjects2 = (from codeObject in solution.GetAllChildren<CodeObject>()
                        select codeObject).AsParallel();
    elapsed = timer.Elapsed.TotalSeconds;
    Log.WriteLine("Found " + codeObjects2.Count() + " CodeObjects in the Solution, elapsed time: " + elapsed.ToString("N6") + " (using PLINQ)");

    // Query ALL Comments in an entire Solution
    var comments = from comment in solution.GetAllChildren<Comment>()
                   select comment;
    Log.WriteLine("Found " + comments.Count() + " Comments in the Solution");

    // Query ALL MethodDecls in an entire Solution, with the criteria that they contain more than 2 'if' statements
    var methodDecls = from methodDecl in solution.GetAllChildren<MethodDecl>()
                      where methodDecl.GetAllChildren<If>().Count() > 2
                      select methodDecl;
    Log.WriteLine("Found " + methodDecls.Count() + " MethodDecls in the Solution that contain more than 2 'if' statements");
}