Improve your .NET code quality with NDepend

An in-depth analysis of .NET Core 3.0 support for WPF and Winforms APIs

.NET Core 3.0 will be RTM soon and it supports WPF and Winforms APIs.

In my last post I’ve been exploring .NET Core 3.0 new APIs by comparing compiled bits with NDepend, of .NET Core 3.0 against .NET Core 2.2.

In this post I will compare .NET Core 3.0 Windows Forms (Winforms) and WPF APIs with .NET Framework 4.x.

I won’t make the suspense last: .NET Core 3.0 support for Winforms and WPF APIs is almost complete, I found very few breaking changes.

I will now explain what I’ve done with NDepend to explore this API diff, and then dig into the results. If you are wondering How to port desktop applications to .NET Core 3.0 see Microsoft explanations here.

Comparing .NET Core 3.0 Winforms and WPF APIs vs. NET Framework 4.x with NDepend

From the NDepend Start Page select Compare 2 versions of a code base menu. Then use Add assemblies in Folder buttons to add .NET Framework assemblies from folder C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319 and Microsoft.WindowsDesktop.app nuget package assemblies (from folder C:\Users\psmac\.nuget\packages\microsoft.windowsdesktop.app\3.0.0-preview-27325-3\ref\netcoreapp3.0 on my machine).

Comparing .NET Core 3.0 Winforms WPF APIs with .NET Framework

A minor difficulty was to isolate the exact set of assemblies to focus on. Here is the list of concerned assemblies I came up with:

Notice that to preserve the correspondance between APIs and assemblies packaging, the attribute TypeForwardedToAttribute is massively used to delegate implementations.

Usage of TypeForwardedToAttribute

The few breaking changes

With the default NDepend rules about API breaking changes, I’ve only found 16 public types and 52 public methods missing. Here are the types:

16 types missing in WPF Winforms .NET Core 3.0 API

16 types missing on a total of 4.095 public types, well done!

Number of public types per assemblies

The 52 public methods missing are: (on a total of 42.645 public methods)

Parent Assembly Name Parent Type Name Method name
System.Security.Principal.Windows System.Security.Principal.WindowsIdentity .ctor(String,String)
System.Security.Principal.Windows System.Security.Principal.WindowsIdentity Impersonate()
System.Security.Principal.Windows System.Security.Principal.WindowsIdentity Impersonate(IntPtr)
System.Security.Principal.Windows System.Security.Principal.IdentityReferenceCollection get_IsReadOnly()
System.Security.Permissions System.Net.EndpointPermission ToString()
System.Security.Permissions System.Security.HostProtectionException GetObjectData(SerializationInfo,StreamingContext)
System.Security.Permissions System.Security.Policy.ApplicationDirectory Clone()
System.Security.Permissions System.Security.Policy.ApplicationTrust Clone()
System.Security.Permissions System.Security.Policy.PermissionRequestEvidence Clone()
System.Security.Permissions System.Security.Policy.Site Clone()
System.Security.Permissions System.Security.Policy.StrongName Clone()
System.Security.Permissions System.Security.Policy.Url Clone()
System.Security.Permissions System.Security.Policy.Zone Clone()
System.Security.Permissions System.Security.Policy.GacInstalled Clone()
System.Security.Permissions System.Security.Policy.Hash Clone()
System.Security.Permissions System.Security.Policy.Publisher Clone()
System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms .ctor(SubjectIdentifierType,ContentInfo)
System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms .ctor(SubjectIdentifierType,ContentInfo,AlgorithmIdentifier)
System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.EnvelopedCms Encrypt()
System.Security.Cryptography.Pkcs System.Security.Cryptography.Pkcs.ContentInfo Finalize()
System.Security.Cryptography.Cng System.Security.Cryptography.ECDiffieHellmanCng FromXmlString(String)
System.Security.Cryptography.Cng System.Security.Cryptography.ECDiffieHellmanCng ToXmlString(Boolean)
System.Security.Cryptography.Cng System.Security.Cryptography.ECDsaCng FromXmlString(String)
System.Security.Cryptography.Cng System.Security.Cryptography.ECDsaCng ToXmlString(Boolean)
System.Security.Cryptography.Cng System.Security.Cryptography.RSACng DecryptValue(Byte[])
System.Security.Cryptography.Cng System.Security.Cryptography.RSACng EncryptValue(Byte[])
System.Security.Cryptography.Cng System.Security.Cryptography.RSACng get_KeyExchangeAlgorithm()
System.Security.Cryptography.Cng System.Security.Cryptography.RSACng get_SignatureAlgorithm()
System.Printing System.Printing.PrintQueue set_Name(String)
System.Printing System.Printing.IndexedProperties.PrintInt32Property op_Implicit(PrintInt32Property)
System.Printing System.Printing.IndexedProperties.PrintStringProperty op_Implicit(PrintStringProperty)
System.Printing System.Printing.IndexedProperties.PrintStreamProperty op_Implicit(PrintStreamProperty)
System.Printing System.Printing.IndexedProperties.PrintQueueAttributeProperty op_Implicit(PrintQueueAttributeProperty)
System.Printing System.Printing.IndexedProperties.PrintQueueStatusProperty op_Implicit(PrintQueueStatusProperty)
System.Printing System.Printing.IndexedProperties.PrintBooleanProperty op_Implicit(PrintBooleanProperty)
System.Printing System.Printing.IndexedProperties.PrintThreadPriorityProperty op_Implicit(PrintThreadPriorityProperty)
System.Printing System.Printing.IndexedProperties.PrintServerLoggingProperty op_Implicit(PrintServerLoggingProperty)
System.Printing System.Printing.IndexedProperties.PrintDriverProperty op_Implicit(PrintDriverProperty)
System.Printing System.Printing.IndexedProperties.PrintPortProperty op_Implicit(PrintPortProperty)
System.Printing System.Printing.IndexedProperties.PrintServerProperty op_Implicit(PrintServerProperty)
System.Printing System.Printing.IndexedProperties.PrintTicketProperty op_Implicit(PrintTicketProperty)
System.Printing System.Printing.IndexedProperties.PrintByteArrayProperty op_Implicit(PrintByteArrayProperty)
System.Printing System.Printing.IndexedProperties.PrintProcessorProperty op_Implicit(PrintProcessorProperty)
System.Printing System.Printing.IndexedProperties.PrintQueueProperty op_Implicit(PrintQueueProperty)
System.Printing System.Printing.IndexedProperties.PrintJobPriorityProperty op_Implicit(PrintJobPriorityProperty)
System.Printing System.Printing.IndexedProperties.PrintJobStatusProperty op_Implicit(PrintJobStatusProperty)
System.Printing System.Printing.IndexedProperties.PrintDateTimeProperty op_Implicit(PrintDateTimeProperty)
System.Printing System.Printing.IndexedProperties.PrintSystemTypeProperty op_Implicit(PrintSystemTypeProperty)
System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingProgressChanged(Object,WritingProgressChangedEventArgs)
System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingCompleted(Object,WritingCompletedEventArgs)
System.Printing System.Windows.Xps.XpsDocumentWriter raise__WritingCancelled(Object,WritingCancelledEventArgs)
System.Drawing System.Drawing.FontConverter Finalize()

Portability to .NET Core 3.0 analysis

Microsoft offers a Portability Analyzer tool to analyze changes in desktop API that will break your desktop app. I’ve tested it on NDepend but I just got very coarse results. Did I miss something? At least it is mostly green 🙂

Portability Analyzer analysis on NDepend

I wrote last year a post named Quickly assess your .NET code compliance with .NET Standard let me know in comment if it is worth revisiting this post for desktop APIs. Btw, my guess is that desktop APIs won’t be part of .NET Standard vNext (since there is no plan to support it on all platforms) but I haven’t found any related info on the web.

Why migrate your desktop app to .NET Core 3.0?

This is a great news that Microsoft embeds good-old desktop APIs in .NET Core 3.0 with such an outstanding compatibility. It is worth noting that so far (February 2019) there is no plan to port Windows Forms and WPF on other platforms than Windows.  So, what are the benefits of porting an existing application to .NET Core 3.0?

I found answers in this recent How to Port Desktop Applications to .NET Core 3.0 Channel9 30 minutes video at 5:12. Basically you’ll get more deployment flexibility, Core Runtime and API improvements and also more performances.

Microsoft promises to not urge anyone to port existing Winforms and WPF application to .NET Core 3.0. However for a Visual Studio extension shop like us if it is decided that VS will run on .NET Core 3.0 in the future, we hope to be notified many months ahead. We discussed that on twitter with Amanda Silver in January 2019. It looks like this spring 2019 they will take a decision. As a consequence to support both Visual Studio past versions running on .NET fx and new versions running on .NET Core 3, an extension will need to support both .NET Fx and .NET Core 3 desktop APIs.

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 (that I finally wrote here).

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.

A Look at .NET Core 2.1

A Look at .NET Core 2.1

The .NET Framework has certainly been through many changes since it was introduced by Microsoft in 2002. Arguably, .NET Core is the biggest change. First, .NET Core is open source. Also, you can now build .NET applications that run on Windows, Linux, and Mac. Developers can choose which packages and frameworks to include in their applications, different from the .NET Framework’s all-or-nothing methodology. .NET Core fundamentally changes how .NET developers write code. Now .NET Core 2.1 will add to the .NET revolution happening right now.

Before we review what .NET Core 2.1 brings to the table, it’s important to mention .NET Standard as well. .NET Standard provides a common set of APIs that each .NET implementation is guaranteed to have. .NET Core has to implement the .NET Standard APIs, so we’ll call out where it’s necessary when something in .NET Core 2.1 is put in because .NET Standard changed.

Faster Builds

Writing software is always easier when you can quickly execute code in order to test it and get fast feedback. Microsoft understands this and certainly has heard that .NET Core’s build times could be improved. That is exactly what Microsoft has done.

A key feature of .NET Core 2.1 is the significant performance improvements when building code. Each incremental build of .NET Core 2.1 has gotten faster, leading to a huge boost in performance from .NET Core 2.0 to 2.1.


This performance increase helps with development speeds as well as build speeds by using automated build tools, such as MSBuild. Large projects especially should see a dramatic increase in the speed of building your application.

Impactful New Features

Even though .NET Core 2.1 is an incremental update, it packs many good features that make it worthwhile to try out.

View Array Data with Span<T>

A big piece of .NET Core 2.1 is the introduction of the new Span<T> type. This type allows you to view pieces of memory and use them without copying what is in the memory. How do you pass the first 1,000 elements of a 10,000 element array? If you’re using 2.0, you have to copy those elements into a new array and then pass the new array into the method. As arrays get larger, this operation becomes a major hit on performance.

The Span<T> type allows you to view and access a certain piece of an array (and other blocks of memory) without copying it. Think of it as a drive-thru window. Instead of going into the entire “store” to access the array elements required, a method can simply drive past the “window” and receive what it needs to do its job.

A really useful feature of the Span<T> type is the slice method. Slice is the way you can create that “window” into an array. Let’s look at an example.

This is a simple example that highlights the basic uses of Span<T>. First, you can create a span from an existing array. You can then slice that span by telling the slice method where in the array to start and how far to go. Then you can use that sliced portion of the array as you see fit without any performance hits. You can check out this example here and here.

Sockets Performance

Sockets are the gateways into your server. They serve as the foundation for incoming and outgoing network communication between computers. Previous versions of .NET Core used native code (such as C) in order to implement sockets. Starting with .NET Core 2.1, sockets are created using a new managed (meaning built using C# itself) class.

There is a new class in town called SocketsHttpHandler. This class will provide access to sockets using .NET sockets and non-native sockets. This has several benefits like the following:

  • Better performance
  • No more reliance on native operating system libraries for socket functionality (requiring a different implementation for each operating system)
  • More consistent behavior across platforms

Self-Contained Applications

A really interesting and useful addition to .NET Core 2.1 is the self-contained publishing of applications. You can now choose the option of a self-contained application when you package an application to prepare it for deployment (called “publishing”). A self-contained application has the .NET Core libraries and runtime included in the package. This means it can be isolated from other applications when it is run. You can have two applications running different versions of .NET Core on the same machine because the necessary version of the runtime is packaged with the application.

This does make the final executable quite large and has some other drawbacks. However, in the right situation, self-contained applications can be quite useful.

New Security Features

Let’s face it, you’ll rarely read a post written by me that doesn’t touch on security. My security geekdom can prove to be useful. .NET Core 2.1 has changed and added some important security features to remain compliant with a new version of .NET Standard just released.

CryptographicOperations Class

The new CryptographicOperations class gives developers two powerful tools in order to increase the security of their applications: FixedTimeEquals and ZeroMemory.

FixedTimeEquals helps to prevent a subtle side-channel attack on login screens. An attacker may try to brute force your login page or try to guess a username and password. Some applications provide a subtle but dangerous clue that allows attackers to know how close they are to the right login information. An attacker will continually enter login credentials, waiting for the response to take a bit longer. That can be a clue that the username is correct but the password is wrong. Attackers use timing attacks to break in.

FixedTimeEquals ensures that any two inputs of the same length will always return in the same amount of time. Use this when doing any cryptographic verification, such as your login functionality, to help prevent timing attacks.

ZeroMemory is a memory-clearing routine that cannot be optimized away by the compiler. This may seem strange, but sometimes the compiler will “optimize” code that clears memory without later reading that memory by eliminating the clearing code. This is better for speed from a technical standpoint. However, this could lead to sensitive secrets, like if cryptographic keys are left in memory without you knowing it.

Other Crypto Fun

Some other cool secure features were added to .NET Core 2.1. First, elliptic-curve Diffie-Hellman (ECDH) is now available on .NET Core. It’s okay if you don’t know what that is. Just know that it is a really good public-key cryptographic algorithm that has great performance and is a great choice for mobile and IoT applications.

Some other improvements include expanding existing cryptographic APIs to work with the new span type, leading to a 15% performance increase for some algorithms. .NET Core 2.1 also has better support overall for the SHA-2 Hash Algorithm.

How to Get It

If you want to play with .NET Core 2.1—frankly, I can’t wait to myself—here’s how to get it. Download the SDK and the runtime so you can build applications using the command line. If you want to use Visual Studio to build .NET Core 2.1, it has to be Visual Studio 2017 15.7 Preview 1. You should also check out the release notes for Preview 1 and Preview 2.

.NET Core 2.1 is incremental in number but big on delivery. The new Span<T> type has driven major performance improvements for the core libraries and will do the same for your application. New security features will help you write more secure code. And new tech is fun. So have fun and try out .NET Core 2.1.

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!