.NET SDK
FeatureBoard .NET SDK for .NET core and framework applications.
Installation
dotnet add package FeatureBoard.DotnetSdk
Setup
Create a Features model
You can either manually create your model or use the cli tool or nx plugin to generate a model from your configuration in FeatureBoard.
cli tool
The FeatureBoard CLI Tool can generate a fully type safe API for your application based on the configuration of your FeatureBoard project.
First run npx @featureboard/cli login
to authenticate, then from a folder containing the target .NET csproj run npx @featureboard/cli code-gen --template dotnet-api --output ./
to generate a Features.cs file in the output path or update an existing one.
nx plugin
- Install the plugin by running
npm install @featureboard/nx-plugin
- Login to FeatureBoard by running
npx @featureboard/cli login
- Generate a Features.cs file by running
npm exec nx generate @featureboard/nx-plugin:code-gen --featureBoardProductName="My Product" --projectName=my-api --template=dotnet-api --subFolder=./
Manual creation
Note that FeatureKeyName
can be used here to apply the correct feature key to a property, otherwise FeatureBoard will do it’s best to convert the property to a kebab case key.
using FeatureBoard.DotnetSdk.Models;
public class WeatherFeatures : IFeatures{ [FeatureKeyName("weather-imperial")] public bool WeatherImperial { get; set; }}
Implement the audience provider
This provides the audiences of the current user or application context. For example it could pull the roles from the User and add the audience key role-admin
, or it could pull a location header which your CDN provides and have country-australia
as an audience.
using FeatureBoard.DotnetSdk;
public class ClaimsAudienceProvider : IAudienceProvider{ public List<string> AudienceKeys { get; }
public ClaimsAudienceProvider(IHttpContextAccessor contextAccessor) { AudienceKeys = contextAccessor.HttpContext?.User.Claims .Where(x => x.Type == "audience") .Select(x => x.Value).ToList() ?? new List<string>(); }}
Then register the IAudienceProvider provider with its corresponding implementation. This allows FeatureBoard to use your custom audience provider. e.g. inside Program.cs :
builder.Services.AddScoped<IAudienceProvider, ClaimsAudienceProvider>();
Register the provider in program.cs
with an update strategy
FeatureBoard SDKs are designed to work with different update strategies to accommodate various application architectures.
Polling strategy (recommended)
This strategy means that the FeatureBoard service is polled for updates on a frequency based on the MaxAge
configuration option in appsettings.json
. Default value is 30 seconds.
var builder = WebApplication.CreateBuilder(args);// Other typical Program.cs configuration entries go here// Register FeatureBoard with polling strategy and the above ClaimsAudienceProviderbuilder.Services.AddScoped<IAudienceProvider, ClaimsAudienceProvider>();builder.Services.AddFeatureBoard<WeatherFeatures>(builder.Configuration) .WithPollingUpdateStrategy();
Request update strategy
This strategy is useful for serverless applications where the VM gets paused between invocations, it will guarantee that the feature values are always up to date for the current invocation at the cost of an additional server call for every request.
var builder = WebApplication.CreateBuilder(args);// Other typical Program.cs configuration entries go here// Register FeatureBoard with polling strategy and the above ClaimsAudienceProviderbuilder.Services.AddScoped<IAudienceProvider, ClaimsAudienceProvider>();builder.Services.AddFeatureBoard<WeatherFeatures>(builder.Configuration) .WithOnRequestUpdateStrategy();
Add middleware
// Add featureBoard middlewareapp.UseFeatureBoard();
Azure Function Support
Azure Function 4.x support under .NET 8 has deprecated In-Process Azure Functions, as such to use .NET 8 with Azure Function 4.x Runtime requires a slight change. Here is an example Program.cs:
var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices((hostContext, services) => { services.AddScoped<IAudienceProvider, ClaimsAudienceProvider>(); // Required for the FeatureBoard HTTP Client services.AddHttpContextAccessor(); services // An IConfiguration Root is required here .AddFeatureBoard<FunctionFeatures>((hostContext.Configuration as IConfigurationRoot)!) .WithPollingUpdateStrategy(); }).ConfigureLogging((hostingContext, logging) => { // Required to bubble out library logging entries - will appear in App Insights logging.AddConsole(); // Customise where appropriate logging.AddConfiguration(hostingContext.Configuration.GetSection("MyLoggingSection")); }) .Build();
host.Run();
Make sure "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
is set. You may also need to add https://www.nuget.org/packages/Microsoft.Extensions.Http support to your project.
BackgroundService Support
Running a stand-alone background service as part of a Console app has a slightly different method of configuration. Given this example Program.cs:
var host = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { services.AddScoped<IAudienceProvider, ClaimsAudienceProvider>(); // Required for the FeatureBoard HTTP Client services.AddHttpContextAccessor(); // An IConfiguration Root is required here services.AddFeatureBoard<Features>((hostContext.Configuration as IConfigurationRoot)!) .WithPollingUpdateStrategy();
// A class that implements BackgroundService. Note: register this last to ensure FeatureBoardSDK has // refreshed values by the time the HostedService starts. Alternatively consider managing the HostedService // started with an IHostApplicationLifetime instance. services.AddHostedService<Worker>(); }) .Build();
host.Run();
You may also need to add https://www.nuget.org/packages/Microsoft.Extensions.Http support to your project.
Add the environment key to appsettings.json
.NET Application example:
{ .... "AllowedHosts": "*", "FeatureBoardOptions": { "EnvironmentApiKey": "YOUR KEY HERE", "MaxAge": "00:00:30" // Optional (defaults to 30 seconds) }}
Azure Function example:
{ "Values": { ... "FeatureBoardOptions__EnvironmentApiKey": "YOUR KEY HERE", "FeatureBoardOptions__MaxAge": "00:00:30" // Optional (defaults to 30 seconds) }}
Usage
using FeatureBoard.DotnetSdk;using FeatureBoardSdks.Examples.DotnetApi.Models;using Microsoft.AspNetCore.Mvc;
namespace FeatureBoardSdks.Examples.DotnetApi.Controllers;
The FeatureBoard client can be injected into your class or controller with dependency injection and used directly to resolve features for the current users audience.
[ApiController][Route("[controller]")]public class IconController : ControllerBase{
private readonly IFeatureBoardClient<Features> _featureBoardClient;
public IconController(ILogger<IconController> logger, IFeatureBoardClient<Features> featureBoardClient) { _featureBoardClient = featureBoardClient; }
[HttpPut(Name = "Icons")] public IActionResult Put(IconUpdate update) { if (!_featureBoardClient.GetFeatureValue(features => features.AllowEdits, false)) { return Unauthorized(); } _repository.UpdateIcon(update) }}
Or if you have generated your features model though the cli you can use the generated FeatureFilter attributes to limit access
[ApiController][Route("[controller]")]public class IconController : ControllerBase{ private readonly IRepository _repository;
public IconController(ILogger<IconController> logger, IRepository _repository) { _featureBoardClient = featureBoardClient; }
[HttpPut(Name = "Icons")] [FeatureFilter(BooleanFeature.AllowEdits, false)] public IActionResult Put(IconUpdate update) { _repository.UpdateIcon(update) }}
External State Store
You can create an external state store in the case where FeatureBoard is unavailable. To do this, implement IFeatureBoardExternalState
.
using FeatureBoard.DotnetSdk.Models;using FeatureBoard.DotnetSdk.States;
public class MyExternalState: IFeatureBoardExternalState{ Task<IReadOnlyCollection<FeatureConfiguration>> GetState(CancellationToken cancellationToken); {....}
Task UpdateState(IReadOnlyCollection<FeatureConfiguration> features, CancellationToken cancellationToken); {....}}
Then register it in Program.cs
.
builder.Services.AddFeatureBoard<WeatherFeatures, QueryStringAudienceProvider>() .WithPollingUpdateStrategy() .WithExternalState<MyExternalState>();