Improve your .NET code quality with NDepend

Exploring .NET Core 3.0 new API

.NET Core 3.0 is representing a major step for the .NET community. It is interesting to analyze what’s new in the API directly from the compiled bits. In this post I will first explain how to diff .NET Core 3.0 against .NET Core 2.2 with NDepend, and then how to browse diff results.

Arguably the biggest progress of .NET Core 3.0 will be the support for Winforms and WPF on the Windows platform. Since everything is new here, compare to .NET Core 2.2, we won’t analyze this part. However it will be interesting to analyze .NET Fx Winforms/WPF APIs vs .NET Core 3.0 Winforms/WPF APIs in another post.

Analyzing two versions of .NET Core with NDepend

It takes a few minutes to download NDepend trial, install it and start VisualNDepend.exe, and it takes a few minutes to compare .NET Core 3.0 against .NET Core 2.2. If you want to browse the diff on your machine, expect 5 to 10 minutes to get hands-on.

First Start VisualNDepend.exe and click Compare 2 versions of a code base:

How to compare 2 versions of a code base

For both builds, choose Add Assemblies in Folder:

  • Choose C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.2  for Older Build
  • Choose C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0-preview-27324-5 for Newer Build
Choose assemblies in folder to analyze

Respectively 156 and 161 assemblies are gathered. Click Ok to run two analysis, on older and newer build. Both analysis results will be then diffed automatically.

Ready to compare 2 versions of .NET Core

Querying new API

Let’s start with a few CQLinq code queries to explore the new .NET Core 3.0 APIs:

This query match all new public code elements, including new assemblies, namespace, types, methods and fields:

Use the NDepend query result to browse this large new API set : 5 new assemblies, 83 new namespaces, 297 new types, 4 924 new methods and 307 new fields. Note that code elements with pink background are not matched by the query, they are just here for preserving the code hierarchy in the result:

.NET Core 3.0 new public API

Download here this long list obtained by exporting the query result to excel. For a better result formatting I actually used this refined query to show properly parent assemblies/namespaces/types in excel columns:

It is interesting to just focus on the 297 new public types with the code query below. Download the list here or browse the same list at the end of this post.

New .NET Core 3.0 Public Types

It is also interesting to browse the new 1.101 public methods and 38 public fields added on public types that existed already in .NET Core 2.2.  Download this list here.

API Breaking Changes

NDepend proposes 6 default rules to browse API breaking changes.

These rules matche 19 public types removed from .NET Core 2.2 (see list below) 176 public methods removed and 36 public fields removed

.NET Core 2.2 Types Removed

Listing Methods Changed

Exploring the API evolution is useful for API consumers. For those working on the framework .NET Core itself, it is interesting to also browse implementation changes. The NDepend search by change panel proposes various options for that. Note that this search panel is actually a code query generator. The Edit query button proposes to edit and refine the currently generated query.

Another interesting point is that it is a semantic implementation change. All matched methods do behave differently at runtime. This makes this tool ideal to plan code change review without bothering with formatting and comments change.

Matched code elements can be highlighted in the metric view. From the screenshot above we can see at a glance that System.Xml and System.Data are much more stable than System.RunTime for example. By zooming in the view, we can get more information about which code was churned.

Highlight methods where code was changed

In the query result panel, a code element is underlined when its implementation changed. If you have compiled both source versions on your machine and analyzed those compiled versions, you can right click an underlined method and directly compare the diff in source code.

I hope you see value both in the results offered and in the how-to-diff procedure that can be applied to any .NET code base, assuming you have 2 versions to compare.

New .NET Core 3.0 types

Here is the list of the 297 new types added to .NET Core 3.0.

ParentAssembly Full Name
WindowsBase System.Windows.Markup .ValueSerializerAttribute
System.Xml.Linq System.Xml.XPath.XDocumentExtensions
System.Threading.ThreadPool System.Threading.IThreadPoolWorkItem
System.Threading.Tasks System.Runtime.CompilerServices .AsyncIteratorMethodBuilder
System.Threading.Tasks System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T>
System.Text.Json System.Text.Json.JsonCommentHandling
System.Text.Json System.Text.Json.JsonTokenType
System.Text.Json System.Text.Json.JsonDocument
System.Text.Json System.Text.Json.JsonElement
System.Text.Json System.Text.Json .JsonElement+ArrayEnumerator
System.Text.Json System.Text.Json .JsonElement+ObjectEnumerator
System.Text.Json System.Text.Json.JsonProperty
System.Text.Json System.Text.Json.JsonValueType
System.Text.Json System.Text.Json.JsonReaderException
System.Text.Json System.Text.Json.JsonReaderOptions
System.Text.Json System.Text.Json.JsonReaderState
System.Text.Json System.Text.Json.Utf8JsonReader
System.Text.Json System.Text.Json.JsonWriterOptions
System.Text.Json System.Text.Json.JsonWriterState
System.Text.Json System.Text.Json.Utf8JsonWriter
System.Security.Principal.Windows System.Security.Principal .WindowsAccountType
System.Security.Cryptography.Primitives System.Security.Cryptography .PbeEncryptionAlgorithm
System.Security.Cryptography.Primitives System.Security.Cryptography .PbeParameters
System.Security.Cryptography.Algorithms System.Security.Cryptography.AesCcm
System.Security.Cryptography.Algorithms System.Security.Cryptography.AesGcm
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.CornerRadius
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.DurationType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Duration
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.GridUnitType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.GridLength
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Thickness
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.LayoutCycleException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Markup .XamlParseException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Automation .ElementNotAvailableException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Automation .ElementNotEnabledException
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Matrix
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Media3D.Matrix3D
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation.KeyTime
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation .RepeatBehaviorType
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Media.Animation .RepeatBehavior
System.Runtime.WindowsRuntime.UI.Xaml Windows.UI.Xaml.Controls.Primitives .GeneratorPosition
System.Runtime.WindowsRuntime Windows.UI.Color
System.Runtime.WindowsRuntime Windows.Foundation.Point
System.Runtime.WindowsRuntime Windows.Foundation.Rect
System.Runtime.WindowsRuntime Windows.Foundation.Size
System.Runtime.WindowsRuntime System.WindowsRuntimeSystemExtensions
System.Runtime.WindowsRuntime System.IO .WindowsRuntimeStorageExtensions
System.Runtime.WindowsRuntime System.IO.WindowsRuntimeStreamExtensions
System.Runtime.WindowsRuntime System.Threading.DispatcherQueueHandler
System.Runtime.WindowsRuntime System.Threading.DispatcherQueuePriority
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime.AsyncInfo
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime.WindowsRuntimeBuffer
System.Runtime.WindowsRuntime System.Runtime.InteropServices .WindowsRuntime .WindowsRuntimeBufferExtensions
System.Runtime.Serialization System.Runtime.Serialization .ISerializationSurrogateProvider
System.Runtime.Loader System.Runtime.Loader .AssemblyDependencyResolver
System.Runtime.Intrinsics System.MidpointRounding
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector64
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector64<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector128
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector128<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector256
System.Runtime.Intrinsics System.Runtime.Intrinsics.Vector256<T>
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86 .FloatComparisonMode
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Aes
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Avx
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Avx2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Bmi1
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Bmi2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Fma
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Lzcnt
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Pclmulqdq
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Popcnt
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse2
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse3
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse41
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Sse42
System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.Ssse3
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Aes
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Base
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Sha1
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64 .Sha256
System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Arm64.Simd
System.Runtime.InteropServices System.Runtime.CompilerServices .IDispatchConstantAttribute
System.Runtime.InteropServices System.Runtime.InteropServices .StandardOleMarshalObject
System.Runtime.InteropServices System.Runtime.InteropServices .NativeLibrary
System.Runtime.Extensions System.AppDomainSetup
System.Runtime.Extensions System.Security.IStackWalk
System.Runtime.Extensions System.Security.PermissionSet
System.Runtime.Extensions System.Security.Permissions .PermissionState
System.Runtime.Extensions System.Runtime.ProfileOptimization
System.Runtime System.ArgIterator
System.Runtime System.IAsyncDisposable
System.Runtime System.Index
System.Runtime System.Range
System.Runtime System.Text.Rune
System.Runtime System.Text.StringRuneEnumerator
System.Runtime System.Globalization.ISOWeek
System.Runtime System.Threading.Tasks.Sources .ManualResetValueTaskSourceCore<TResult>
System.Runtime System.Runtime.Remoting.ObjectHandle
System.Runtime System.Runtime.CompilerServices .AsyncIteratorStateMachineAttribute
System.Runtime System.Runtime.CompilerServices .CallerArgumentExpressionAttribute
System.Runtime System.Collections.Generic .IAsyncEnumerable<T>
System.Runtime System.Collections.Generic .IAsyncEnumerator<T>
System.Private.CoreLib System.IAsyncDisposable
System.Private.CoreLib System.Index
System.Private.CoreLib System.Range
System.Private.CoreLib System.Text .StringBuilder+ChunkEnumerator
System.Private.CoreLib System.Text.Rune
System.Private.CoreLib System.Text.SpanRuneEnumerator
System.Private.CoreLib System.Text.StringRuneEnumerator
System.Private.CoreLib System.Globalization.ISOWeek
System.Private.CoreLib System.Buffers.OperationStatus
System.Private.CoreLib System.Buffers.StandardFormat
System.Private.CoreLib System.Buffers.Text.Utf8Formatter
System.Private.CoreLib System.Buffers.Text.Utf8Parser
System.Private.CoreLib System.Buffers.Binary.BinaryPrimitives
System.Private.CoreLib System.Threading.Tasks.Sources .ManualResetValueTaskSourceCore<TResult>
System.Private.CoreLib System.Runtime.Remoting.ObjectHandle
System.Private.CoreLib System.Runtime.Loader .AssemblyDependencyResolver
System.Private.CoreLib System.Runtime.CompilerServices .AsyncIteratorMethodBuilder
System.Private.CoreLib System.Runtime.CompilerServices .AsyncIteratorStateMachineAttribute
System.Private.CoreLib System.Runtime.CompilerServices .CallerArgumentExpressionAttribute
System.Private.CoreLib System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T>
System.Private.CoreLib System.Runtime.CompilerServices .ConfiguredCancelableAsyncEnumerable<T >+Enumerator
System.Private.CoreLib System.Runtime.Intrinsics.Vector64
System.Private.CoreLib System.Runtime.Intrinsics.Vector128
System.Private.CoreLib System.Runtime.Intrinsics.Vector256
System.Private.CoreLib System.Runtime.Intrinsics.X86.Bmi1+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Bmi2+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Lzcnt+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Popcnt+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse2+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse41+X64
System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse42+X64
System.Private.CoreLib System.Runtime.InteropServices .DllImportResolver
System.Private.CoreLib System.Runtime.InteropServices .NativeLibrary
System.Private.CoreLib System.Runtime.InteropServices .ComActivationContext
System.Private.CoreLib System.Runtime.InteropServices .ComActivationContextInternal
System.Private.CoreLib System.Runtime.InteropServices .ComActivator
System.Private.CoreLib System.Runtime.InteropServices .ComEventInterfaceAttribute
System.Private.CoreLib System.Runtime.InteropServices .DefaultParameterValueAttribute
System.Private.CoreLib System.Diagnostics.DebugProvider
System.Private.CoreLib System.Collections.Generic .IAsyncEnumerable<T>
System.Private.CoreLib System.Collections.Generic .IAsyncEnumerator<T>
System.Private.CoreLib Internal.Resources.PRIExceptionInfo
System.Private.CoreLib Internal.Resources .WindowsRuntimeResourceManagerBase
System.Private.CoreLib Internal.Threading.Tasks .AsyncCausalitySupport
System.Private.CoreLib Internal.Runtime.InteropServices .WindowsRuntime.ExceptionSupport
System.ObjectModel System.Reflection.ICustomTypeProvider
System.ObjectModel System.ComponentModel .TypeConverterAttribute
System.ObjectModel System.ComponentModel .TypeDescriptionProviderAttribute
System.ObjectModel System.Windows.Markup .ValueSerializerAttribute
System.Net.Sockets System.Net.Sockets.SafeSocketHandle
System.Memory System.Text.SpanRuneEnumerator
System.Memory System.Buffers.SequenceReader<T>
System.Memory System.Buffers.SequenceReaderExtensions
System System.StringNormalizationExtensions
System System.Reflection.ICustomTypeProvider
System System.Windows.Markup .ValueSerializerAttribute
System System.Runtime.InteropServices .StandardOleMarshalObject
System System.Diagnostics.ConsoleTraceListener
System System.Diagnostics .XmlWriterTraceListener
System System.Diagnostics.StackFrameExtensions
System System.Security.SecureStringMarshal
System System.Net.Sockets .SocketReceiveFromResult
System System.Net.Sockets .SocketReceiveMessageFromResult
System System.Net.Sockets.SocketTaskExtensions
System.Diagnostics.TextWriterTraceListener System.Diagnostics.ConsoleTraceListener
System.Diagnostics.TextWriterTraceListener System.Diagnostics .XmlWriterTraceListener
System.Data System.Xml.XmlDataDocument
System.Data System.Data.Common.DbColumn
System.Data System.Data.Common .DbDataReaderExtensions
System.Data System.Data.Common .IDbColumnSchemaGenerator
System.Core System.Security.Cryptography.AesCng
System.Core System.Security.Cryptography.DSACng
System.Core System.Security.Cryptography .TripleDESCng
System.Core System.Security.Cryptography.ECCurve
System.Core System.Security.Cryptography .ECParameters
System.Core System.Security.Cryptography.ECPoint
System.Core System.Security.Cryptography .IncrementalHash
System.Core System.Security.Cryptography .X509Certificates.CertificateRequest
System.Core System.Security.Cryptography .X509Certificates .DSACertificateExtensions
System.Core System.Security.Cryptography .X509Certificates .SubjectAlternativeNameBuilder
System.Core System.Security.Cryptography .X509Certificates.X509SignatureGenerator
System.ComponentModel.TypeConverter System.ComponentModel.VersionConverter
System.ComponentModel.Primitives System.ComponentModel .InvalidAsynchronousStateException
System.ComponentModel.DataAnnotations System.ComponentModel.DataAnnotations .AssociatedMetadataTypeTypeDescriptionPr ovider
System.ComponentModel.DataAnnotations System.ComponentModel.DataAnnotations .MetadataTypeAttribute
System.ComponentModel.Annotations System.ComponentModel.DataAnnotations .AssociatedMetadataTypeTypeDescriptionPr ovider
System.ComponentModel.Annotations System.ComponentModel.DataAnnotations .MetadataTypeAttribute
mscorlib System.TupleExtensions
mscorlib System.AppDomainSetup
mscorlib System.Globalization .GlobalizationExtensions
mscorlib System.Threading.ThreadPoolBoundHandle
mscorlib System.Threading.PreAllocatedOverlapped
mscorlib System.Runtime.ProfileOptimization
mscorlib System.Runtime.Remoting.ObjectHandle
mscorlib System.Runtime.CompilerServices .RuntimeFeature
mscorlib System.Runtime.CompilerServices .IsByRefLikeAttribute
mscorlib System.Runtime.CompilerServices.ITuple
mscorlib System.Runtime.CompilerServices .IsReadOnlyAttribute
mscorlib System.Runtime.CompilerServices .TupleElementNamesAttribute
mscorlib System.Runtime.CompilerServices .IDispatchConstantAttribute
mscorlib System.Runtime.InteropServices .RuntimeInformation
mscorlib System.Runtime.InteropServices .Architecture
mscorlib System.Runtime.InteropServices .OSPlatform
mscorlib System.Reflection.Emit.DynamicILInfo
mscorlib System.Security.IStackWalk
mscorlib System.Security.PermissionSet
mscorlib System.Security.Permissions .PermissionState
mscorlib System.Security.Principal .WindowsAccountType
mscorlib System.Diagnostics.Tracing .EventSourceCreatedEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic.Collection
Microsoft.VisualBasic Microsoft.VisualBasic.CompareMethod
Microsoft.VisualBasic Microsoft.VisualBasic.ComClassAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.ControlChars
Microsoft.VisualBasic Microsoft.VisualBasic.DateAndTime
Microsoft.VisualBasic Microsoft.VisualBasic.Information
Microsoft.VisualBasic Microsoft.VisualBasic .MyGroupCollectionAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.VariantType
Microsoft.VisualBasic Microsoft.VisualBasic .VBFixedArrayAttribute
Microsoft.VisualBasic Microsoft.VisualBasic .VBFixedStringAttribute
Microsoft.VisualBasic Microsoft.VisualBasic.VBMath
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.FileSystem
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .DeleteDirectoryOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .RecycleOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .SearchOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .UICancelOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.UIOption
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .MalformedLineException
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .SpecialDirectories
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO .TextFieldParser
Microsoft.VisualBasic Microsoft.VisualBasic.FileIO.FieldType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .BooleanType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .DecimalType
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .Versioned
Microsoft.VisualBasic Microsoft.VisualBasic.CompilerServices .DoubleType
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices.StartupEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices .StartupNextInstanceEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic .ApplicationServices .UnhandledExceptionEventArgs
Microsoft.VisualBasic Microsoft.VisualBasic.Devices .NetworkAvailableEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CallType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Collection
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompareMethod
Microsoft.VisualBasic.Core Microsoft.VisualBasic.ComClassAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Constants
Microsoft.VisualBasic.Core Microsoft.VisualBasic.ControlChars
Microsoft.VisualBasic.Core Microsoft.VisualBasic.DateAndTime
Microsoft.VisualBasic.Core Microsoft.VisualBasic .HideModuleNameAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Information
Microsoft.VisualBasic.Core Microsoft.VisualBasic .MyGroupCollectionAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Strings
Microsoft.VisualBasic.Core Microsoft.VisualBasic.VariantType
Microsoft.VisualBasic.Core Microsoft.VisualBasic .VBFixedArrayAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic .VBFixedStringAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.VBMath
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.FileSystem
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .DeleteDirectoryOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .RecycleOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .SearchOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .UICancelOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.UIOption
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .MalformedLineException
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .SpecialDirectories
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO .TextFieldParser
Microsoft.VisualBasic.Core Microsoft.VisualBasic.FileIO.FieldType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .BooleanType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Conversions
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DecimalType
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DesignerGeneratedAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate0
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate1
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate2
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate3
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate4
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate5
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate6
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .SiteDelegate7
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .IncompleteInitialization
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .NewLateBinding
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ObjectFlowControl
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ObjectFlowControl+ForLoopControl
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Operators
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .OptionCompareAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .OptionTextAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .ProjectData
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .StandardModuleAttribute
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .StaticLocalInitFlag
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Utils
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .Versioned
Microsoft.VisualBasic.Core Microsoft.VisualBasic.CompilerServices .DoubleType
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices.StartupEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices .StartupNextInstanceEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic .ApplicationServices .UnhandledExceptionEventArgs
Microsoft.VisualBasic.Core Microsoft.VisualBasic.Devices .NetworkAvailableEventArgs

Advanced Code Search : A Case Study

This morning I stumbled on a complex test to write. The need was to create and show a custom Form (written with Windows Form) that relies on the System.ComponentModel.BackgroundWorker to do initialization stuff without freezing the UI. The test is complex because after creating and showing the form, it must wait somehow to release the UI thread for a while to let the BackgroundWorker achieve the RunWorkerCompleted on the UI thread.

I know that this is something we’ve done in the past and I know this is tricky enough to not reinvent the wheel. But with a test suite of over 13.000 tests this is quite challenging to find where we did that. So I decided to use NDepend querying facility to search.

First I analyze all NDepend assemblies, test assemblies included. Then I generate a code query to match all classes that derive from Form. This can be done from the NDepend Search panel : search Form by name in third-party types and then use a right-click menu to generate the code query:

The CQLinq code query generated is:

60 classes are matched:

Let’s refine this query to match all methods that create any of those form classes.This could be achieved by iterating over (all methods) x (all form classes), but the NDepend.API extension method ThatCreateAny() acts like a join and operates in a linear time. For our search scenario, waiting a few seconds to get a search result is not a problem. But for a code rule written with CQLinq, this is important to run it as fast as possible in a few milliseconds, to run all queries and rules often in Visual Studio within a few seconds, hence the query performance entry on the documentation.

280 methods are instantiating some form classes. Let’s refine the query to match only tests method. The cleanest way would be to check for the usage of TestAttribute, but here just checking for parent assemblies names that contain “Test” is enough:

Still 122 test methods matched.

Before filtering the result even more, let’s refine the query to display for each test the form class(es) it instantiates. This can be achieved with a LINQ range variable formsCreated that we use in the result:

We can now browse which form(s) are instantiated by each test:

Finally let’s browse only tests that use some asynchronous related code. Many ways can be used to check for asynchronous usages. The easiest way is certainly to look at methods called by a test method, and check which ones have named related to async stuff. I tried a few words like “Async” “Sync” “Thread” “TimeOut” “Wait”… and “Wait” worked:

In the source code of the highlighted test I had everything I needed for my scenario, including a link to a tricky stackoverflow answer that we found years ago. I found what I needed within a few minutes and had a bit of fun. I hope the methodology and the resulting query can be adapted to your advanced search scenarios.

Quickly assess your .NET code compliance with .NET Standard

Yesterday evening I had an interesting discussion about the feasibility of migrating parts of the NDepend code to .NET Standard to ultimately run it on .NET Core. We’re not yet there but this might make sense to run at least the code analysis on non Windows platform, especially for NDepend clones CppDepend (for C++), JArchitect (for Java) and others to come.

Then I went to sleep (as every developers know the brain is coding hard while sleeping), then this morning I went for an early morning jogging and it stroke me: NDepend is the perfect tool to  assess some .NET code compliance to .NET Standard, or to any other libraries actually! As soon on my machine I did a proof of concept in less than an hour.

The key is that .NET standard 2.0 types and members are all packet in a single assemblies netstandard.dll v2.0 that can be found under C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll (on my machine).  A quick analyze of netstandard.dll with NDepend shows 2 317 types in 78 namespaces, with 24 303 methods and 884 fields. Let’s precise that netstandard.dll doesn’t contain any code, it is a standard not an implementation. The 68K IL instructions represent the IL code for throw null which is the method body for all non-abstract methods.

.NET Standard 2.0 analyzed by NDepend

(Btw, I am sure that if you read this  you have an understanding of what is .NET Standard but if anything is still unclear, I invite you to read this great article by my friend Laurent Bugnion wrote 3 days ago A Brief History of .NET Standard)

Given that, what stroke me this morning is that to analyze some .NET code compliance to .NET Standard, I’d just have to include netstandard.dll in the list of my application assemblies and write a code query that  filters the dependencies the way I want. Of course to proof test this idea I wanted to explore the NDepend code base compliance to .NET Standard:

NetStandard assembly included in the NDepend assemblies to analyze

The code query was pretty straightforward to write. It is written in a way that:

  • it is easy to use to analyze compliance with any other library than .NET standard,
  • it is easy to explore the compliance and the non-compliance with a library in a comprehensive way, thanks to the NDepend code query result browsing facilities,
  • it is easy to refactor the query for querying more, for example below I refactor it to assess the usage of third-party non .NET Standard compliant code

The result looks like that and IMHO it is pretty interesting. For example we can see at a glance that NDepend.API is almost full compliant with .NET standard except for the usage of System.Drawing.Image (all the 1 type are the Image type actually) and for the usage of code contracts.

NDepend code base compliance with .NET standard

For a more intuitive assessment of the compliance to .NET Standard we can use the metric view, that highlights the code elements matched by the currently edited code query.

  • Unsurprisingly NDepend.UI is not compliant at all,
  • portions of NDepend.Core non compliant to .NET Standard are well defined (and I know it is mostly because of some UI code here too, that we consider Core because it is re-usable in a variety of situations).

With this information it’d be much easier to plan a major refactoring to segregate .NET standard compliant code from the non-compliant one, especially to anticipate hot spots that will be painful to refactor.

The code query to assess compliancy can be refactored at whim. For example I found it interesting to see which non-compliant third-party code elements were the most used. So I refactored the query this way:

Without surprise UI code that is non .NET Standard compliant popups first:

.NET Standard non-compliant third-party code usage

There is no limit to refactor this query to your own need, like assessing usage of non-compliant code — except UI code– for example, or assessing the usage of code non compliant to ASP.NET Core 2 (by changing the library).

Hope you’ll find this content useful to plan your migration to .NET Core and .NET Standard!

A problem with extension methods

We like extension methods. When named accordingly they can both make the caller code clearer, and isolate static methods from classes on which they operate.

But when using extension methods, breaking change can happen, and this risk is very concrete, it actually just happened to us.

Since 2012, NDepend.API proposes a generic Append() extension:

Two default rules use this extension method: Avoid namespaces dependency cycles and Avoid types initialization cycles

Last month, on Oct 17th 2017, Microsoft released .NET Framework v4.7.1 that implements .NET Standard 2.0. Around 200 .NET Standard 2.0 were missing in .NET Framewok v4.6.1, and one of those missing API is:

Within NDepend, rules, quality gates, trend metrics … basically everything, is a C# LINQ query stored as textual and compiled and executed on-the-fly. Since the compilation environment uses both namespaces NDepend.Helpers and System.Linq, when running NDepend on top of the .NET Framework v4.7.1, both Append() extension methods are visible. As a consequence, for each query calling the Append() method, the compiler fails with:

Hopefully a user notified us with this problem that we didn’t catch yet and we just released NDepend v2017.3.2 that fixes this problem Only one clean fix is possible to make it compatible with all .NET Framework versions: refactor all calls to the Append() extension method,  into a classic static method invocation, with an explanatory comment:

We expect support on this within the next weeks and months when more and more users will run the .NET Fx v4.7.1 while not changing their rules-set. There is no lesson learnt, this situation can happen and it happens rarely, this shouldn’t prevent you from declaring and calling extension methods. The more mature the frameworks you are relying on, the less likely it’ll happen.

Static analysis of .NET Core 2.0 applications

NDepend v2017.3 has just been released with major improvements. One of the most requested features, now available, is the support for analyzing .NET Core 2.0 and .NET Standard 2.0 projects. .NET Core and its main flavor, ASP.NET Core, represents a major evolution for the .NET platform. Let’s have a look at how NDepend is analyzing .NET Core code.

Resolving .NET Core third party assemblies

In this post I’ll analyze the OSS application ASP.NET Core / EntityFramework MusicStore hosted on github. From the Visual Studio solution file, NDepend is resolving the application assembly MusicStore.dll and also two test assemblies that we won’t analyze here. In the screenshot below, we can see that:

  • NDepend recognizes the .NET profile, .NET Core 2.0, for this application.
  • It resolves several folders on the machine that are related to .NET Core, especially NuGet package folders.
  • It resolves all 77 third-party assemblies referenced by MusicStore.dll. This is important since many code rules and other NDepend features take into account what the application code is using.

It is worth noticing that the .NET Core platform assemblies have high granularity. A simple website like MusicStore references no fewer than 77 assemblies. This is because the .NET Core framework is implemented through a few NuGet packages that each contain many assemblies. The idea is to release the application only with needed assemblies, in order to reduce the memory footprint.

.NET Core 2.0 third party assemblies granularity

NDepend v2017.3 has a new heuristic to resolve .NET Core assemblies. This heuristic is based on .deps.json files that contain the names of the NuGet packages referenced. Here we can see that 3 NuGet packages are referenced by MusicStore. From these package names, the heuristic will resolve third-party assemblies (in the NuGet store) referenced by the application assemblies (MusicStore.dll in our case).

NuGet packages referenced in .deps.json file

Analyzing .NET Standard assemblies

Let’s be clear that NDepend v2017.3 can also analyze .NET Standard assemblies. Interestingly enough, since .NET Standard 2.0, .NET Standard assemblies reference a unique assembly named netstandard.dll and found in C:\Users\[user]\.nuget\packages\NETStandard.Library\2.0.0\build\netstandard2.0\ref\netstandard.dll.

By decompiling this assembly, we can see that it doesn’t contain any implementation, but it does contain all types that are part of .NET Standard 2.0. This makes sense if we remember that .NET Standard is not an implementation, but is a set of APIs implemented by various .NET profiles, including .NET Core 2.0, the .NET Framework v4.6.1, Mono 5.4 and more.

Browsing how the application is using .NET Core

Let’s come back to the MusicStore application that references 77 assemblies. This assembly granularity makes it impractical to browse dependencies with the dependency graph, since this generates dozens of items. We can see that NDepend suggests viewing this graph as a dependency matrix instead.

NDepend Dependency Graph on an ASP.NET Core 2.0 project

The NDepend dependency matrix can scale seamlessly on a large number of items. The numbers in the cells also provide a good hint about the represented coupling. For example, here we can see that  22 members of the assembly Microsoft.EntityFrameworkCore.dll are used by 32 methods of the assembly MusicStore.dll, and a menu lets us dig into this coupling.

NDepend Dependency Matrix on an ASP.NET Core 2.0 project

Clicking the menu item Open this dependency shows a new dependency matrix where only members involved are kept (the 32 elements in column are using the 22 elements in rows). This way you can easily dig into which part of the application is using what.

NDepend Dependency Matrix on an ASP.NET Core 2.0 project

All NDepend features now work when analyzing .NET Core

We saw how to browse the structure of a .NET Core application, but let’s underline that all NDepend features now work when analyzing .NET Core applications. On the Dashboard we can see code quality metrics related to Quality Gates, Code Rules, Issues and Technical Debt.

NDepend Dashboard on an ASP.NET Core 2.0 project

Also, most of the default code rules have been improved to avoid reporting false positives on .NET Core projects.

NDepend code rules on an ASP.NET Core 2.0 project

We hope you’ll enjoy using all your favorite NDepend features on your .NET Core projects!

Our experience with using third-party libraries

NDepend is a tool that helps .NET developers write beautiful code. The project was started in April 2004. It is now used by more than 6 000 companies worldwide.

In more than a decade, many decisions were made, each with important consequences on the code base evolution process, sometime good consequences, sometime less good. Relentlessly dog-fooding (i.e using NDepend to analyze and improve the NDepend code base) helped us a lot to obtain more maintainable code, less bugs, and to improve the tool usability and features.

When it comes to working on and maintaining a large code base for several years, some of the most important decisions made are related to relying (or not) on some third-party libraries. Choosing whether or not to rely on a library is a double-edged sword decision that can, over time, bring a lot of value or cause a lot of friction. Ultimately users won’t make a difference between your bugs and third-party libraries bugs, their problems become your problems. Consider this:

  • Sometimes the team just needs a fraction of a library and it may be more cost-effective, and more maintainable with time, to develop your own.
  • Sometimes the licensing of a free or commercial library will prevent you from achieving your goals.
  • Sometimes the library looks bright and shiny but becomes an obsolete project a few months later and precious resources will have to be spent maintaining others code, or migrating toward a trendy new library.
  • Sometimes the library code and/or authors are so fascinating that you’ll be proud to contribute and be part of it.

Of course we all hope for the last case, and we had the chance to experience this a few times for real. Here are some libraries we’ve had great success with:

Mono.Cecil (OSS)

Mono.Cecil is an open source project developed by Jean-Baptiste Evain that can read and write data nested in .NET assemblies, including compiled IL code. NDepend has relied on Cecil for almost a decade to read IL code and other data. Both the library and the support provided are outstanding. The performance of the library is near optimal and all bugs reported were fixed in a matter of days or even hours. For our usage, the library is actually close to bug free. If only all libraries had this level of excellence…

DevExpress WinForm (Commercial)

NDepend has also relied on DevExpress WinForm for almost a decade to improve the UI look and feel. NDepend is a Visual Studio extension and DevExpress WinForm makes smooth visual integration with Visual Studio. Concretely, thanks to this library we achieved the exact same Visual Studio theming and skinning, docking controls a la Visual Studio, menus, bars and special controls like BreadCrumb to explore directories. We have never been disappointed with DevExpress WinForm. The bugs we reported were fixed quickly, it supports high DPI ratio and it is rock solid in production.

Microsoft Automatic Graph Layout MSAGL (OSS)

NDepend has relied on MSAGL for several years to draw all sorts of graphs of dependencies between code elements including Call Graphs, Class Inheritance Graphs, Coupling Graphs, Path and Cycle Graphs…  This library used to be commercial but nowadays OSS.  Here also the bugs we reported were fixed quickly, it supports high DPI ratio and it is perfectly stable in production.

NRefactory (OSS)

NDepend used to have a C# Code Query LINQ editor in 2012, a few years before Roslyn became RTM. We wanted to offer users a great editing experience with code completion and documentation tooltips on mouse-hovering. At that time NRefactory was the best choice and it proved with the years to be stable in production. Nowadays Roslyn would certainly be a better choice, but since our initial investment NRefactory still does the job well, we didn’t feel the need (yet) to move to Roslyn.


Here are a few things we prefer to keep in-house:


While there are libraries for licensing, these are vital, sensitive topics that require a lot of flexibility with time, and we preferred to avoid delegating it. This came at the cost of plenty of development/support and significant levels of acquired expertise. Even taking into account that these efforts could have been spent on product features, we still don’t regret our choice.

The licensing layer is a cornerstone in our relation with our users community and it cannot suffer from any compromise. As a side remark, several times I observed that the cost of developing a solid licensing layer postponed promising projects to become commercial for a while.


As most of application, NDepend persists and restores a lot of data, especially the large amount of data in analysis results. We could have used relational or object databases, but for a UI tool embedded in VS, the worst thing would be to slow down the UI and make the user wait. We decided only optimal performance is acceptable for our users, and optimal performance in persistence means using custom streams of bytes. The consequence of this decision is less flexibility: each time our data schema evolves, we need to put in extra effort to keep ascendant compatibility.

I underline that most of the time it is not a good idea to develop a custom persistence layer because of the amount of work and expertise required. But taken account our particular needs and goals, I think we took the right decision.

Production Logs

I explained here about our production logs system. We consider it an essential component to making NDepend a super-stable product. Here also, we could have used a third-party library. We capitalize on our own logging system because, year after year, we customized it with a plethora of production information, which was required to help fix our very own problems. We kept the system lightweight and flexible, and it still helps us improve the overall stability and correctness of our products.

Dependency Matrix and Treemap UI Components

These UI components were developed years ago and are still up to date. Both then and now, I believe there is no good third-party alternative that meets all our requirements in terms of layout and performance. A few times, we received propositions to buy those components, but we are not a component provider and don’t have plans for that.


In this post I unveiled a few core choices we made over the years. We hope this information will be useful for other teams.

exploring technical debt codebase

Exploring the Technical Debt In Your Codebase

Recently, I posted about how the new version of NDepend lets you compute tech debt.  In that post, I learned that I had earned a “B” out of the box.  With 40 minutes of time investment, I could make that an “A.”  Not too shabby!

In that same post, I also talked about the various settings in and around “debt settings.”  With debt settings, you can change units of debt (time, money), thresholds, and assumptions of working capacity.  For folks at the intersection of tech and business, this provides an invaluable way to communicate with the business.

But I really just scratched the surface with that mention.  You’re probably wondering what this looks like in more detail.  How does this interact with the NDepend features you already know and love?  

Well, today, I’d like to take a look at just that.

To start, let’s look at the queries and rules explorer in some detail.

Introducing Quality Gates

Take a look at this screenshot, and you’ll notice some renamed entries, some new entries, and some familiar ones.

In the past, “Code Smells” and “Code Regressions” had the names “Code Quality” and “Code Quality Regression,” respectively.  With that resolved, the true newcomers sit on top: Quality Gates and Hot Spots.  Let’s talk about quality gates.

Continue reading Exploring the Technical Debt In Your Codebase

Managing Code Analysis Statistics with the NDepend API

If you’re familiar with NDepend, you’re probably familiar with the Visual Studio plugin, the out of the box metrics, the excellent visualization tools, and the iconic Zone of Uselessness/Zone of Pain chart.  These feel familiar to NDepend users and have likely found their way into the normal application development process. NDepend has other features as well, however, some of which I do not necessarily hear discussed as frequently.  The NDepend API has membership in that “lesser known NDepend features club.”  Yes, that’s right — if you didn’t know this, NDepend has an API that you can use.

You may be familiar, as a user, with the NDepend power tools.  These include some pretty powerful capabilities, such as duplicate code detection, so it stands to reason that you may have played with them or even that you might routinely use them.  But what you may not realize is the power tools’ source code accompanies the installation of NDepend, and it furnishes a great series of examples on how to use the NDepend API.

NDepend’s API is contained in the DLLs that support the executable and plugin, so you needn’t do anything special to obtain it.  The NDepend website also treats the API as a first class citizen, providing detailed, excellent documentation.   With your NDepend installation, you can get up and running quickly with the API.

Probably the easiest way to introduce yourself is to open the source code for the power tools project and to add a power tool, or generally to modify that assembly.  If you want to create your own assembly to use the power tools, you can do that as well, though it is a bit more involved.  The purpose of this post is not to do a walk-through of setting up with the power tools, since that can be found here.  I will mention two things, however, that are worth bearing in mind as you get started.

  1. If you want to use the API outside of the installed project directory, there is additional setup overhead.  Because it leverages proprietary parts of NDepend under the covers, setup is more involved than just adding a DLL by reference.
  2. Because of point (1), if you want to create your own assembly outside of the NDepend project structure, be sure to follow the setup instructions exactly.

A Use Case

I’ve spoken so far in generalities about the API.  If you haven’t already used it, you might be wondering what kinds of applications it has, besides simply being interesting to play with.  Fair enough.

One interesting use case that I’ve experienced personally is getting information out of NDepend in a customized format.  For example, let’s say I’m analyzing a client’s codebase and want to cite statistical information about types and methods in the code.  Out of the box, what I do is open Visual Studio and then open NDepend’s query/rules editor.  This gives me the ability to create ad-hoc CQLinq queries that will have the information I need.

But from there, I have to transcribe the results into a format that I want, such as a spreadsheet.  That’s fine for small projects or sample sizes, but it becomes unwieldy if I want to plot statistics in large codebases.  To address this, I have enlisted the NDepend API.

Continue reading Managing Code Analysis Statistics with the NDepend API

4 Ways Custom Code Metrics Improve A Development Team

One of the things that has surprised me over the years is how infrequently people take advantage of custom code metrics.  I say this not from the perspective of a geek with esoteric interest in a subject, wishing other people would share my interest.  Rather, I say this from the perspective of a business man, making money, and wondering why I seem to have little competition.

As I’ve mentioned before, a segment of my consulting practice involves strategic code assessments that serve organizations in a number of ways.  When I do this, the absolute most important differentiator is my ability to tailor metrics to the client and specific codebases on the fly.  Anyone can walk in, install a tool, and say, “yep, your cyclomatic complexity in this class is too high, as evidenced by this tool I installed saying ‘your cyclomatic complexity in this class is too high.'”  Not just anyone can come in and identify client-specific idiosyncrasies and back those findings with tangible data.

But, if they would invest some up-front learning time in how to create custom code metrics, they’d be a lot closer.

Being able to customize code metrics allows you to reason about code quality in very dynamic and targeted terms, and that is valuable.  But you might think that, unless you want a career in code base assessment, value doesn’t apply to you.  Let me assure you that it does, albeit not in a quite as direct way as it applies to me.

Custom code metrics can help make your team better and they can do so in a variety of ways.  Let’s take a look at a few.

Continue reading 4 Ways Custom Code Metrics Improve A Development Team

The Power of CQLinq for Developers

I can still remember my reaction to Linq when I was first exposed to it.  And I mean my very first reaction.  You’d think, as a connoisseur of the programming profession, it would have been, “wow, groundbreaking!”  But, really, it was, “wait, what?  Why?!”  I couldn’t fathom why we’d want to merge SQL queries with application languages.

Up until that point, a little after .NET 3.5 shipped, I’d done most of my programming in PHP, C++ and Java (and, if I’m being totally honest, a good bit of VB6 and VBA that I could never seem to escape).  I was new to C#, and, at that time, it didn’t seem much different than Java.  And, in all of these languages, there was a nice, established pattern.  Application languages were where you wrote loops and business logic and such, and parameterized SQL strings were where you defined how you’d query the database.  I’d just gotten to the point where ORMs were second nature.  And now, here was something weird.

But, I would quickly realize, here was something powerful.

The object oriented languages that I mentioned (and whatever PHP is) are imperative languages.  This means that you’re giving the compiler/interpreter a step by step series of instructions on how to do something.  “For an integer i, start at zero, increment by one, continue if less than 10, and for each integer…”   SQL, on the other hand, is a declarative language.  You describe what you want, and let something else (e.g. the RDBMS server) sort out the details.  “I want all of the customer records where the customer’s city is ‘Chicago’ and the customer is less than 40 years old — you figure out how to do that and just give me the results.”

And now, all of a sudden, an object oriented language could be declarative.  I didn’t have to write loop boilerplate anymore!

Continue reading The Power of CQLinq for Developers