Marking API's as obsolete or as experimental

03/11/2022

Often times your API in your program or library evolves. So you will need a mechanism for telling that a specific API (an interface or just a simple method call) is obsolete and might be not there anymore in the next major version.

Also, it can happen that you have a preview version of an API, which might not be rock-stable and the API-surface might change. How do we indicate that to the user?

The simple answer: Attributes. .NET knows two special ones in this case:

  • ObsoleteAttribute
  • RequiresPreviewFeaturesAttribute

They are a bit special.

ObsoleteAttribute

The ObsoleteAttribute is in the Framework since almost the beginning. It marks that the element should not be used anymore. The attribute is special because the compiler knows of it and treats it in a certain way. Here is a quick example of how to use it:

OldWarning(); // Generates warning (or error if you enable treat warnings as errors)
OldError(); // Generates errors

[Obsolete($"Take {nameof(New)} instead", false)]
void OldWarning() { }

[Obsolete($"Take {nameof(New)} instead", true)]
void OldError() { }

void New() { }

The first argument is a message to a user, which you can use to indicate, which new method he should use instead. The second one is about whether or not the compiler should treat it as a warning (which is the default, if you don't provide that parameter) or even as an error. The compiler tells you the following: "CS0618: Local function 'OldWarning()' is obsolete: Take New instead".

So you can make a 3 step approach:

  1. Mark it as warning
  2. Mark it as error
  3. Remove it entirely

By the way the attribute can also be set on a class, property, interface or struct. You are not limited to use it on methods.

RequiresPreviewFeaturesAttribute

The RequiresPreviewFeaturesAttribute is used to indicate that a feature is in a preview state. Feature can mean a simple method or a whole class or interface. The usage is roughly the same:

New();

[RequiresPreviewFeatures("API might change")]
void New() { }

If you hover over the New() call you get the message: "API might change". If you try to compile that, you get even an error: "Program.cs(1, 1): [CA2252] API might change Using 'New' requires opting into preview features. See https://aka.ms/dotnet-warnings/preview-features for more information."

The resolve that you have to opt-in into preview features. This is done on project level:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <TargetFramework>net7.0</TargetFramework>

        <!-- This will opt in into all preview features -->
        <EnablePreviewFeatures>true</EnablePreviewFeatures>
    </PropertyGroup>

Unfortunately it isn't very fine grained. You don't have control over which features you want to use. The (maybe ugly) alternative is using pragmas:

#pragma warning disable CA2252
New();
#pragma warning restore CA2252

In any way, this is until now (.net 7) the only way of marking an API as a preview. In contrast to the ObsoleteAttribute, which is handled by the compiler, the RequiresPreviewFeaturesAttribute is handled via Roslyn-Analyzer.

Conclusion

I showed you a way of marking your APIs as old so you can delete them gracefully later and I also showed you how to mark them as experimental so your user knows, that the API surface is not stable.

Anonymous test data with AutoFixture

Often times we have unit or integration tests that rely on some input data. The easiest solution is just to take some hard-coded values and move on with life. This has some major downsides:

Giving specific values in a test carries meaning, but we are often times not interested in that. We just need to pass the object around to fulfill the API. Also the simplest solution to fulfill your test is literally checking against those values.

Here is an elegant solution to that problem: AutoFixture. I will show you what it can do, especially in combination with xUnit.

Reusable loading Bar Component in Blazor

In our Blazor components we often call a repository or web API which takes some time. So it is nice to indicate to an user what the current state is. That is why we will create a small indicator even with steps to indicate how far we are in the progress.

Fluent API to await multiple calls and get their respective results

Sometimes, you have multiple async calls to make, and you want to do that asynchronously and get the results afterward. Let's build a fluent API to do that.

An error has occurred. This application may no longer respond until reloaded. Reload x