C# Nullable Reference Types: IntelliSense Confusion

The feature and concept of Nullable reference types were introduced in C# 8.0 and it basically made all types non-nullable by default and ensured that these types could never be assigned the value null. This is one of my favorite features in C# recently, but there are scenarios where a mixed nullable environment could cause confusion.

Confusion

To enable the assignment of the value null to a type, you have to explicitly mark that type. This uses the same concept of nullable as introduced in C# 2.0, where you, for example, make an int nullable by adding a question mark after it: int?.

When we look at a regular example service-class, we can see which benefits can be had from Nullable reference types:

public class ProductService
{
    // This method accepts a non-null string for 'productId'
    // and always returns a string
    public string FormatProductId(string productId)
    {
        // ...
    }

    // This method accepts a nullable 'formattedProductId'
    // and returns a string or null
    public string? TryGetProductName(string? formattedProductId)
    {
        // ...
    }
}

This makes things all fine and clear. We know that the method FormatProductId never returns null and that it doesn't accept null in its parameter. We also know that the method TryGetProductName returns a string which could be null and that the parameter accepts a string which could be null.

This is great, this means that we don't have to perform a null-check on productId-parameter of the FormatProductId-method, right? Well, not exactly...

Confusion: Mixed nullable environments

In an environment where all your code has Nullable reference types enabled, you can trust the output of a method and the input to its parameters. In a mixed nullable environment, things are not as straight forward, especially when you look at how IntelliSense in Visual Studio signals what to expect from the code.

Scenario 1: Modern app & legacy library

Imagine that your new modern app has Nullable reference types enabled, but you're using an external library that is legacy and does not have this enabled. This external library can be your own old library or something you've included from NuGet.

The problem now becomes that the external library is signaling, for example, that it has a method that returns a string and not a string?, so you should be able to trust that it is not null, right? Unfortunately not. Even with a local non-nullable project, IntelliSense tells me that the returned string is not null, even when it is.

Value is not null

Scenario 2: Legacy app & modern library

Imagine that you have just put together a nice library that you want others to use, either in your project, organization or publicly through NuGet. One of the best parts about using Nullable reference types is that the compiler will warn you if you try to send in a null value as a parameter to a method that explicitly states that it doesn't support null.

Nice, now you can clean out all those noisy null-checks at the top of all the methods, right? Unfortunately not. Your code might be used by another assembly (or an older version of Visual Studio), which doesn't detect the non-nullability.

In a way, this means you have to reverse the way you do null-checks in your code.

public class ProductService
{
    // This method does not accept a null-value
    // and if it does, it should throw an exception
    public string FormatProductId(string productId)
    {
        if (productId == null)
            throw new ArgumentNullException(productId);
        // ...
    }

    // This method accepts null-values
    // and should adjust its logic accordingly
    public string? TryGetProductName(string? formattedProductId)
    {
        return
            formattedProductId != null
            ? GetProductName(formattedProductId)
            : null;
    }
}

Key takeaways

My own take-aways from exploring this aspect of Nullable reference types are:

  • When building a library, always check for null in incoming method-arguments, even when Nullable reference types is enabled
  • When consuming an external legacy library, don't trust the return-type to not be null (even if it says it's not)
  • In a mixed nullable environment, the feature to guard us against NullReferenceExceptions is likely to mistakenly cause some more of them
  • When this feature is fully adopted, there will be a reduction in a lot of the overhead in null-handling code

Thoughts

Hopefully, in .NET 5, this feature is enabled by default and these kinds of confusions, and described associated errors can be avoided.

One idea for an improvement to the IntelliSense-behavior around assemblies that are not known to have Nullable reference types enabled could be to show all these types as nullable. Both because it makes things super-clear, but also because of the fact that they actually are nullable.

This change would make everything in the whole .NET Core CLR light up as nullable, but as of .NET Core 3.1, it all is nullable, by definition.

ASP.NET: Transform Web.config with Debug/Release on Build

Web.config Transformation

Did you ever wish you could run your web application with the Web.config being transformed according to the current Solution Configuration for Debug or Release directly from Visual Studio? Well, with some pre-build-magic we can do this.

Files in project

Create a file named Web.Base.config, besides the existing Web.Debug.config and Web.Release.config. This file will be the equivalent to your old Web.config, as it will be the base for the tranformation. You will end up with these files:

  • Web.config
  • Web.Base.config
  • Web.Debug.config
  • Web.Release.config

Web.config

Add the following configuration to the bottom of your .csproj-file, just before the closing </Project>-tag:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" />
<Target Name="BeforeBuild">
    <TransformXml Source="Web.Base.config" Transform="Web.$(Configuration).config" Destination="Web.config" />
</Target>

UPDATE: From a reminder in the comments I realized that I also have a problem with Visual Studio transforming the XML twice, when Publishing a project. The solution to this is to add a Condition to the Target-tag like this:

<Target Name="BeforeBuild" Condition="'$(PublishProfileName)' == '' And '$(WebPublishProfileFile)' == ''">

You can now build, run your project or publish it all depending on what Solution Configuration you have selected. Debug or Release. Just press your CTRL+SHIFT+B and see the Web.config-file be transformed.

Version control

I recommend that you leave Web.config out of your Version Control. This solution for transforming the Web.config on the fly would mean that this specific file would constantly be modified and if one person is working with Debug and another person is working with Release the Web.config would probably get big changes constantly.

Testing-tool

The people over at elmah.io has created the Web.config Transformation Tester, where you can try out what the result will be of combining a Web.config-file with a transformation.

The tool can even show you what the diff from the original file is when the transformation is applied.

Making MVC 3 Razor IntelliSense work after installing MVC 4 Beta

ASP.NET

After installing the MVC 4 Beta the IntelliSense breaks for Razor-views in MVC 3-applications in Visual Studio 2010. This is stated in the release-notes, but nobody usually reads those.

This time the solution to the problem are actually listed in those release-notes. You need to the explicitly state the version-numbers of the references in your web.config.

Add a new appSettings-entry for explicitly stating the version of WebPages to use:

<appSettings>
    <add key="webpages:Version" value="1.0.0.0"/>
    <!-- ... -->
</appSettings>

Then you have to edit your .csproj-file, where you need to find your references to System.Web.WebPages and System.Web.Helpers and makes sure they have the explicit version-numbers like this:

<Reference Include="System.Web.WebPages, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/> 
<Reference Include="System.Web.Helpers, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />

Hopefully this will be resolved in the final version of MVC 4 or maybe the case is that the references to versions in Razor v1 were just too loose in MVC 3-projects.

Visual Studio 2010: Dropped Assembly References Workaround

After adding some references to external assemblies like to NLog or Munq I found out that on some projects Visual Studio 2010 had a tendency to drop the reference to some of these assemblies.

After the usual searching of Google and Stackoverflow I've put together a combination of different solutions that seems to solve this issue.

  • Step 1: Expand your references and bring up the properties-window. Set Specific Version to True:
  • Step 2: Right click on the project and click Untload Project:
  • Step 3: Right click again on the project and click Edit ...csproject.
  • Step 4: Add an XML-element with the name SpecificVersion with the value True beneath the reference in question:
  • Step 5: Right click on the project and click Reload Project.

This works for me so far.

Visual Studio: C#-Settings & StyleCop

Design Guidelines for .NET development is something very close and dear to me. Partially for my own sake, but mostly to have consistency across a company, between friends and maybe even across blog-examples.

The tool StyleCop is Microsoft's tool for enforcing these guidelines.

StyleCop analyzes C# source code to enforce a set of style and consistency rules. It can be run from inside of Visual Studio or integrated into an MSBuild project.

After asking a question Visual Studio C#-settings and StyleCop (MS Source Analysis) on Stackoverflow I set out to try to get C# in Visual Studio to behave as compliant as possible.

The result after some testing around was this Visual Studio settings file. You can easily import these settings to your own Visual Studio.

It's mainly about placements of parentheses and curly-braces.

Yes, it can be a bit of a hassle to get used to having the curly-braces on the same line as the namespace-declaration, the class-declaration and method-declaration, but it saves some space and I got used to it quiet quickly and now have problems going back.

namespace TestApp {
    public class TestObject {
        static TestObject() {
            if(true) {
                DoSomething();
                DoSomething2();
            } else {
                DoSomethingElse()
            }
        }
    }
}

This is as close as I can get to the conventions. It's worth to note that StyleCop can differ a bit from the book Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries .