The other day I introduced Query Editor and provided instructions to extract the sample queries embedded in the PowerCommands for Reflector 1.3 release. In this entry I’ll provide an introduction for writing your own queries.
Writing the simplest query: (List all loaded assemblies)
This is sort of a ‘Hello World’ for Query Editor, since it really doesn’t get any easier than this. I’ll assume you already have the PowerCommands addin loaded in Reflector and have enabled the Query Editor command (if you haven’t read this entry first).
To get started, you’ll need to open Query Editor (Tools menu –> Query Editor –> Display Query Editor). This will open the editor and should look something like the screen shot below:
By default the blank new query will have access to the AssemblyManager object - this is the tree view on the left side (called the assembly list in this entry). The AssemblyManager has a property named Assemblies which is of type IAssemblyCollection. To get a list of the current assemblies, we’ll use the AssemblyManager as our data source.
In order to write a query against the IAssemblyCollection (and almost all other collections in the Reflector.CodeModel), you will need to use the Cast<TResult>() extension method (which you can read more about here: Enumerable.Cast<TResult> Method). The query to get all the loaded assembly names would be:
from a in AssemblyManager.Assemblies.Cast<IAssembly>()
If you type the above query into Query Editor and click the build button ( ), you should get a “Build Succeeded” message in the status bar.
Now if you click the Execute button ( ), the query should execute and the results should be listed in the output window listing all the assembly names you have loaded. Something like this:
How does this work?
At this point you may be wondering how the Query Editor ‘executed’ your query. When you ‘build’ or ‘execute’ a query it takes the query text you entered and builds a C# class that is loaded by the PowerCommands addin when you call execute. For example, the generated method that creates the output window results shown above will look like this:
public IEnumerable<string> ExecuteQueryForConsole()
var results = from a in AssemblyManager.Assemblies.Cast<IAssembly>()
foreach (var item in results)
yield return item.ToString();
As you can see, the Query Editor ‘query’ is really just a LINQ (to Objects) query in a generated type that has a property named AssemblyManager (and optionally ActiveItem – discussed later).
The Query Editor has three ways to retrieve the output: Linked List View, Output window and Text file – which can be quickly changed using the tool buttons: or the Configure Dialog. The Output window is the default.
The Output window works great when you have strings being returned – like the above query that returned the assembly’s name property. If you click on the configure button ( ), you will get a dialog allowing you to change the output type (shown below). Go ahead and change the Output Type to ‘Linked List View’ and click OK.
Or optionally, you could have just clicked on the ‘Results to ListView’ button ( ).
Now execute the query again, and you’ll get the Linked List View. Note that the links are broken:
The Linked List View does not work for strings (like the assembly’s name being returned in the query) – but instead works for the following types: IAssemblyReference, IModule, IResource, ITypeReference, IMemberReference, IFieldReference, IMethodReference, IPropertyReference, IEventReference. If you change the above query to return the assembly (not the name), then the result will be an IAssembly which is an IAssemblyReference. So if you remove the ‘.Name’ from the select line and execute the query again, you should get a Linked List View that will now work (click on an item in the list and it will take you to the member in the assembly list).
Changing the QueryType
In the configure dialog above, you may have noticed the Query Type drop down. By default, this is set to ‘Assembly Manager’, which gives you access to the assembly list. If you choose any other Query Type (ie. Assembly), you will get an additional item to use in your LINQ query named ‘ActiveItem’. The ActiveItem is exposed to Reflector addins by the IAssemblyBrowser interface and represents the current selected item in the assembly list. Using a specific Query Type allows you to use some context in your queries – instead of always looping through all loaded assemblies.
For example, say you want to list all types in the selected assembly that have the Serializable attribute assigned to them. To find out if a type has an attribute of a specific name in the Reflector you need to know the following about the Reflector API:
- An assembly has modules (IModule)
- Modules have types (ITypeDeclaration)
- Types can have attributes (ICustomAttribute)
- In order to find an attribute’s type name you need to cast its declaring type to an ITypeReference
So if you have an assembly selected, you’ll need a LINQ query like this to list the types with the Serializable attribute:
from m in ActiveItem.Modules.Cast<IModule>()
from t in m.Types.Cast<ITypeDeclaration>()
from ca in t.Attributes.Cast<ICustomAttribute>()
where ((ITypeReference)ca.Constructor.DeclaringType).Name == "SerializableAttribute"
Notice the first line uses ActiveItem as an IAssembly type this time. This means an assembly needs to be selected in the assembly list before the query can successfully run. If you have something else selected at the time you execute the query you will get an error message in the output window (usually ‘ActiveItem not valid for QueryType’).
If you type in the LINQ query above and try to build it or execute it you will probably first get the message:
Line 1: The name 'ActiveItem' does not exist in the current context
This is a compiler error which means you haven’t set the QueryType to have an ActiveItem property generated yet … so you need to click the configure button and select ‘Assembly’ for the Query Type. Once you do that, you should be able to build and execute the query.
When I build the query and select the System.Web assembly on my machine, I get the following results after executing the query indicating that there are only 48 types in that assembly marked with the Serializable attribute:
Those couple of queries introduced the majority of the Query Editor functionality. In those two examples, you learned about:
- How to create your own query in the Query Editor
- How to set the Output Type
- How to get the Linked List View output type to work
- How to set the Query Type
- A little about the Reflector code model (API)
In the next entry, I’ll show some more complicated queries and get into more detail of the Reflector API.