CQLinq, Code Query LINQ, is a feature proposed by the tool NDepend since the version 4, to query .NET code through LINQ queries.
A CQLinq query is a LINQ query that relies on types defined in the namespace NDepend.CodeModel of the NDepend.API. CQLinq syntax peculiarities are:
CQLinq defines a few predefined domains to query on including:
These domains can be seen as variables of type
The syntax is as simple as:
from m in Methods where m.NbLinesOfCode > 30 select mA CQLinq query can rely on one or several domains. Notice in the query above how the domain word Methods is highlighted differently.
There is a predefined domain named context of type ICQLinqExecutionContext that is the root of all others predefined domains. For example the domain Methods is actually converted by the CQLinq compiler to the expression context.CodeBase.Methods:
from m in context.CodeBase.Methods where m.NbLinesOfCode > 30 select mThe type of the domain context is reserved and thanks to the other predefined domains, there is no need to use context.
There is also a predefined domain named codeBase of type ICodeBase that is converted to context.CodeBase:
from m in codeBase.Methods where m.NbLinesOfCode > 30 select m
There are two convenient predefined domains that are used often: Application and ThirdParty. As their name suggest, these domains are useful to enumerate code elements defined only in application assemblies, or only defined in third-party assemblies (like mscorlib.dll, System.dll or NHibernate.dll) and used by the application code. These two domains are of type ICodeBaseView and represent each a partial view of the entire code base.
from m in Application.Methods where m.NbLinesOfCode > 30 select mThe domains Application and ThirdParty are converted to context.CodeBase.Application and context.CodeBase.ThirdParty.
Notice how the interface ICodeBase extends the interface ICodeBaseView, since the code base can be seen as a total view on itself.
Thanks to the richness of the NDepend.CodeModel namespace, it is easy to refine these predefined domains.
For example the query below matches large methods defined only in the namespace ProductName.FeatureA and its child namespaces:
from m in Application.Namespaces.WithNameLike("ProductName.FeatureA").ChildMethods()
Defining the code base view JustMyCode with notmycode prefix
There is another convenient predefined domain named JustMyCode of type ICodeBaseView. The domain JustMyCode is converted to context.JustMyCode.
The domain JustMyCode represents a facility of CQLinq to eliminate generated code elements from CQLinq query results.
For example the following query will only match large methods that are not generated by a tool (like a UI designer):
from m in JustMyCode.Methods where m.NbLinesOfCode > 30 select mThe set of generated code elements is defined by CQLinq queries prefixed with the CQLinq keyword notmycode.
For example the query below matches methods defined in source files whose name ends up with .designer.cs. These are file generated by some UI designer like the Windows Form designer:
notmycode from m in Methods whereThe CQLinq queries runner executes all notmycode queries before queries relying on JustMyCode, hence the domain JustMyCode is defined once for all. Obviously the CQLinq compiler emits an error if a notmycode query relies on the JustMyCode domain.
See the default notmycode queries here. You can adjust them to your needs.
Defining Description and HowToFix texts
A description can be embedded in a CQLinq query source code.
To do so just use the tags <Description>...</Description> in the query comments. These two tags can be on two different lines.
The Query operator and Query expression syntaxes
Since the CQLinq syntax is based on the C# LINQ syntax, both the query operator syntax and the query expression syntax are allowed.
The query operator syntax is the one with direct calls to System.Linq.Enumerable extension methods like Where() and Select()...
Methods.Where(m => m.NbLinesOfCode > 30)The query expressions syntax is the one with special C# LINQ keywords like where and select...
from m in Methods where m.NbLinesOfCode > 30 select mOften you'll find convenient to mix both syntaxes in one query.
from m in Application.Types.Where(t => t.IsStatic).ChildMethods()
CQLinq query result formatting
The CQLinq query result formatting is constrained. A query result can be a simple numeric scalar value like in the query above:
Methods.Where(m => m.NbLinesOfCode > 30).Count()Or the query result can be an anonymous type, whose first property is of type IType, IMethod, IField, INamespace or IAssembly,
and additional properties (if any) are of type:
from m in Application.Methods
Matching code elements by name string
The NDepend CodeModel API used by CQLinq presents three different kind of naming of code elements: full naming, regular naming and simple naming. The table below summarizes the three different properties involved in naming, and the different naming value for some popular .NET framework code elements.
Notice that the interfaces INamespace and IAssembly don't implement IMember hence there is no full naming of namespace and assemblies.
Also the interfaces IField and IAssembly don't implement ISimpleNamed hence there is no simple naming of fields and assemblies.
With this naming system, it is easy to write CQLinq queries to match some code elements by name:
from t in ThirdParty.Types where
Notice that the static class ExtensionMethodsNaming presents some convenient extension methods like WithFullNameIn(), that make code elements matching by name even easier:
from t in Types.WithFullNameIn("System.IDisposable", "System.String", "System.Object") select t
Some properties of NDepend API and some of the ExtensionMethodsNaming extension methods are specialized in name matching through regular expression. Notice here the CQLinq compiler magic that occurs to make sure that the regular expression passed in argument, is just compiled once for all code elements listed. Notice as well the regular expression suffix \i for ignore case like regular expressions. To use \i the string constant must be verbatim (i.e prefixed with the character @).
from t in Types.WithNameLike("ist") where
Finally, some of the ExtensionMethodsNaming extension methods are specialized in name matching with simple wildcard patterns, with the star * wildcard character:
from t in Types.WithFullNameWildcardMatch("System.I*") select t
Defining query targets
Hence, to write generic queries that virtually compile and run on any code base (no matter if the interface System.IDisposable is used or not), you can use the string extension method AllowNoMatch(), that will prevent compilation error, and make the Implement() expression always returns false if the code elements cannot be matched by name:
from t in Types where t.Implement("System.IDisposable".AllowNoMatch()) select tIf a query target name matches several code elements of the same kind (like several methods or several types) all these code elements are considered as query target.
If a query target name matches several code elements of different kinds, like for example the "System" string can match both the System assembly and the System namespace, a query compilation error occurs. To resolve such situation, there are special extension methods like MatchAssembly() that forces the matching to occurs only with a particular kind of code elements:
from t in Types where t.IsUsing("System".MatchAssembly()) select t
Defining range variables with let
The C# LINQ syntax present the facility to define range variables with the C# keyword let. In this section, we wanted to underline this possibility because using the let keyword is a common practice when writing CQLinq queries.
For example, the following default rule define a custom code metrics thanks to several range variables:
// <Name>C.R.A.P method code metric</Name>Notice that using many let clauses in the main query loop, can significantly decrease performance of the query execution as explained here.
Beginning a query with let
The CQLinq compiler extends the usage of the C# LINQ let keyword, because with CQLinq, the let keyword can be used to define a variable at the beginning of a CQLinq query.
For example, the default CQLinq rule below, first tries to match the System.IDisposable types, and if found, second let the query be executed.
// <Name>Types with disposable instance fields must be disposable</Name>
For some others CQLinq rules, it can be convenient to define multiple sub-sets through several let keyword expressions, before executing the query itself.
For example, the default CQLinq rules below, first define the sub-sets uiTypes and dbTypes before using them in the query code.
// <Name>UI layer shouldn't use directly DB types</Name>
Defining a procedure in a query
With the LINQ syntax it is possible to create a procedure in a query. This is useful if you wish to invoke such procedure from different locations in the query.
This possibility is illustrated in the default rule below where the procedure to check if a type can be considered as a dead type needs to be invoked from two different locations:
// <Name>Potentially dead Types</Name>
CQLinq trend metrics
It is possible to define a Trend Metric with CQLinq. More info on this can be found here.
// <TrendMetric Name="# Lines of Code" Unit="LoC" />
CQLinq quality gates
It is possible to define a Quality Gate with CQLinq. More info on this can be found here.
// <QualityGate Name="New Debt since Baseline" Unit="man-days" />
Types usable in a CQLinq query
Not all types are usable in a CQLinq query because internally, the CQLinq compiler and runner are optimized to work with a defined set of types. You'll find in this list all types needed to query a code base: