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:
In the Explorer tool window, click Add connection.
Click on View more drivers.
In the NuGet LINQPad Manager dialog:
- Select Show all drivers.
- Type Metalama.
- Select
Metalama.LinqPad
and click Install. - Accept a few disclaimers and wait. Then click Close.
Opening a project or solution
In the Explorer tool window, click Add connection.
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.
- Metalama Workspace is bound to a .NET project or solution, which is accessible through the
Choose the Metalama Workspace or Metalama Scratchpad driver and click Next.
If you have chosen Metalama Workspace, specify the path to the C# project or solution, then click Ok.
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:
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.