Open sandboxFocusImprove this doc

Inspecting a project using LINQPadPermalinks

LINQPad is a widely used tool for interactively querying databases using Language Integrated Query (LINQ) syntax. It enables you to write and execute LINQ queries against various data sources, including SQL databases, XML documents, and .NET objects. With the Metalama driver for LINQPad, you can also query your source code like a database.

Benefits

We developed the Metalama drivers for LINQPad to assist developers and architects in building and testing their Metalama aspects and fabrics. However, this driver can be utilized even if you are not using Metalama.

With this driver, you can:

  • Test code queries using the same Metalama.Framework.Code API as the one used in aspects and fabrics,
  • List the target declarations of aspects,
  • Inspect the outcome of aspects, i.e., the output code transformations, diagnostics, or child aspects,
  • Query the resulting code mode.
Note

The Metalama.LinqPad package is open-source.

Installing the Metalama drivers

To install the Metalama driver for LINQPad, follow these steps:

  1. In the Explorer tool window, click Add connection.

    Install step 1

  2. Click on View more drivers.

    Install step 2

  3. In the NuGet LINQPad Manager dialog:

    1. Select Show all drivers.
    2. Type Metalama.
    3. Select Metalama.LinqPad and click Install.
    4. Accept a few disclaimers and wait. Then click Close.

    Install step 3

Opening a project or solution

  1. In the Explorer tool window, click Add connection.

    Install step 1

    As you can see, there are two Metalama drivers:

    • Metalama Workspace is bound to a .NET project or solution, which is accessible through the workspace variable.
    • Metalama Scratchpad is not bound to anything, so you need your project or solution manually.
  2. Choose the Metalama Workspace or Metalama Scratchpad driver and click Next.

    Add connection 1

  3. If you have chosen Metalama Workspace, specify the path to the C# project or solution, then click Ok.

    Add connection 2

Warning

The version of the Metalama.LinqPad driver must be higher or equal to the version of the Metalama.Framework package used in projects.

Querying source code

Upon adding a C# project or solution to LINQPad, you should see the following structure:

Structure 1

The root object, accessible through the workspace variable, allows you to query the entire workspace in a single query, i.e., all projects for all target frameworks.

To see all projects loaded in the workspace, use the workspace.Projects expression.

The workspace object exposes the IProjectSet interface. It has the following properties:

  • The workspace.SourceCode expression gives you access to the source code of the workspace, before Metalama is executed. For instance, workspace.SourceCode.Types is the list of all types in the workspace.

    Note

    If your projects target multiple frameworks, the same declarations will appear multiple times in the queries -- once per target framework.

  • The workspace.TransformedCode object represents the code after Metalama is executed, typically with introduced declarations.

  • The workspace.Diagnostics collection lists errors, warnings, and other messages reported by the C# compiler, Metalama, or any aspect.

  • The AspectClasses, AspectLayers, AspectInstances, Advice, and Transformations collections expose the different steps of the Metalama pipeline.

For more about the code model, see the Metalama.Framework.Workspaces and Metalama.Framework.Introspection namespaces.

Filtering projects

As mentioned above, the workspace object gives a unified view of all projects, which can be confusing in multi-targeted solutions. Here are three solutions when you want to focus on fewer projects.

Querying a single project

If you want to query a single project, the easiest approach is to use the GetProject method, and pass the project name without extension as a parameter. This method returns an object implementing the same IProjectSet interface.

For instance, this gives the set of static fields in the CodeQualityTalk project:

workspace
.GetProject(@"CodeQualityTalk")
.SourceCode
.Fields
.Where( f => f.IsStatic )

Getting a project subset

To work on multiple projects, you can use the GetSubset method and supply a predicate that filters the projects.

For instance, this selects the static fields in all projects targeting .NET Standard 2.0:

workspace
.GetSubset( p => p.TargetFramework == "netstandard2.0" )
.SourceCode
.Fields
.Where( f => f.IsStatic )

Filtering projects in the workspace

Another approach is to apply the filters directly to the workspace object, which is mutable. You can use the ApplyFilter and ClearFilters() methods.

workspace.ApplyFilter( p => p.TargetFramework == "netstandard2.0" );
workspace.SourceCode.Types.Dump();

In the data grid view, you will see that all declarations have a permalink column. Clicking on this link will open a new query that directly evaluates to this declaration, using the SerializableDeclarationId to uniquely identify declarations.

For instance, this is the permalink for the field _diagnosticDescriptor in the FactoryNameAnalyzer type of the CodeQualityTalk.Analyzers project.

workspace.GetDeclaration(
"CodeQualityTalk.Analyzers",
"netstandard2.0",
"F:CodeQualityTalk.Analyzers.FactoryNameAnalyzer._diagnosticDescriptor",
false);

Inspecting code references

You can query inbound and outbound references of any declaration using the GetInboundReferences and GetOutboundReferences methods.

  • Inbound references are references to the current declaration,
  • Outbound references are references from the current declaration.

For instance, the following snippet gets all methods and constructors referencing the field, identified by its permalink.

var field = workspace.GetDeclaration(
   "CodeQualityTalk.Analyzers", 
   "netstandard2.0", 
   "F:CodeQualityTalk.Analyzers.FactoryNameAnalyzer._diagnosticDescriptor", 
   false);

field.GetInboundReferences().Dump();

Metalama Scratchpad: specifying the project name in the query

Instead of using the Metalama Workspace driver, which requires you to specify the C# project or solution in the connection configuration, you can use the Metalama Scratchpad driver.

The Metalama Scratchpad driver does not require you to specify any project in the connection. Therefore, you must define the workspace variable yourself.

Typically, you will start your query with the WorkspaceCollection class, then get the Default property and call the Load method to load your project or solution.

For instance, the following code defines the workspace variable and gives it an identical meaning to if you were using the driver:

var workspace = WorkspaceCollection.Default
.Load(@"C:\src\Metalama.Samples\examples\log\log-3\Metalama.Samples.Log3.csproj");

Querying code without LINQPad

If you want to run a Metalama query from a different application than LINQPad, you must start by adding a reference to the Metalama.Framework.Workspaces package.

Then, you can write code just as if you were coding with the Metalama Scratchpad driver in LINQPad.

You can take this demo project on GitHub as an example.