:::: MENU ::::

Saturday, May 16, 2020

This is the first part in a series.

As the creator of the open source FeatureToggle library for .NET it was with some interest that I learned about Microsoft's own offering to the feature toggle/feature flags landscape.

Microsoft's offering is know as ".NET Core Feature Management" and they use the term "feature flag" in place of other terms such as "feature toggle", "feature flappers", etc. The root namespace is "Microsoft.FeatureManagement".

Getting Started with Microsoft Feature Management in ASP.NET Core

As a simple example, first create a new ASP.NET Core 3.1 web app project with MVC support and then install the Microsoft.FeatureManagement.AspNetCore NuGet package.

Next, open the Startup.cs class, add a using directive for Microsoft.FeatureManagement and opt in to feature management:

1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
 
    services.AddFeatureManagement();
}

Feature flags can be configured in a number of ways, such as in appsettings.json, environment variables, and Azure App Configuration.

Open up the appsettings.json file and add a feature to be managed in a new FeatureManagement section, for example the following defines a feature called "Printing":

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "FeatureManagement": {
    "Printing": true   
  }
}

In the preceding json, the Printing feature is currently enabled.

Now we have a configured feature flag/toggle we can make use of it.

Programmatically Querying Feature Flags in Controllers

If you want to execute conditional logic in a controller based on a feature flag you can do this by first getting a reference to an IFeatureManager. To do this, it can be injected as a dependency into the controller via its constructor:

1
2
3
4
5
6
private readonly IFeatureManager _featureManager;
 
public HomeController(IFeatureManager featureManager)
{
    _featureManager = featureManager;
}

Now the IFeatureManager instance can be used to make decisions by calling the IsEnabledAsync method and providing the string of the feature that was configured in the appsettings.json – in this case "Printing":

1
2
3
4
5
6
7
8
9
10
11
12
13
public async Task<IActionResult> Index()
{
    if (await _featureManager.IsEnabledAsync("Printing"))
    {
        ViewData["PrintMessage"] = "On";
    }
    else
    {
        ViewData["PrintMessage"] = "Off";
    }
     
    return View();
}

In the view you could have some HTML to output the contents of the PrintMessage viewdata:

1
2
3
4
5
6
7
8
@{
    ViewData["Title"] = "Home Page";
}
 
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Printing is currently @ViewData["PrintMessage"]</p>
</div>

In the controller you could of course do more complex logic such as calling additional services or swapping out different algorithms based on flags.

One thing I don't like about the FeatureManagement library is that if a flag is not configured or has a typo in the configuration, then the app still runs with the feature disabled. By default I think that the app should error if  a flag is used but not defined, otherwise you may have important features/logic that does not get executed; I feel that the sooner you know about these kind of problems the better. For example some important legal/compliance text might not be shown when it should.

Managing Controllers and Actions Based On Feature Flags

Controller actions (and also entire controllers) can be enabled/disabled based on a feature flag. For example an action could be based on the printing flag by decorating the action method with the [FeatureGate] attribute as follows:

1
2
3
4
5
[FeatureGate("Printing")]
public IActionResult Print()
{
    return View();
}

If you try and navigate to the Print page when the flag is disabled then you'll get a 404.

You can also apply the [FeatureGate] attribute at the controller class level and this will affect all actions contained therein.

Modifying The Generated HTML View Based On Flags

Sometimes you will have UI elements that should be shown or hidden based on a flag.

To control an entire block of HTML you can surround it with the <feature> tag. To enable this you should first modify _ViewImports.cshtml and add the tag helper as shown below:

1
2
3
4
@using WebApplication1
@using WebApplication1.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Microsoft.FeatureManagement.AspNetCore

As an example, suppose we have disabled the Print page/action – we would also not want it to be displayed in the menu. We could modify the HTML to make the menu item display conditional on the Printing flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
    <div class="container">
        <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">WebApplication1</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
            <ul class="navbar-nav flex-grow-1">
                <li class="nav-item">
                    <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                </li>
                <feature name="Printing">
                    <li class="nav-item">
                        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Print">Print Preview</a>
                    </li>
                </feature>
            </ul>
        </div>
    </div>
</nav>

Notice in the preceding HTML that the <feature name="Printing"> element is wrapped around the printing menu option <li>.

Now if the Printing flag is set to true the menu item will be shown, otherwise it won't appear in the rendered HTML.

More