Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IFeatureDefinitionProvider for selecting features from SQL Server

Tags:

c#

.net-core

Using the FeatureManagement feature in .NET Core 3.1, I am trying to write a custom database feature provider that will pull features and whether or not they are enabled from a SQL Server database. To do this, the Microsoft documentation says you need to implement the IFeatureDefinitionProvider interface.

You need to return a FeatureDefinition class which doesn't contain whether or not the feature is enabled but contains IEnumerable<FeatureFilterConfiguration>. There are no examples anywhere online as the feature is pretty new, and even looking at the Azure implementation (one of their two suggested implementations along with appsettings.json which will not work for this specific use case) is pretty confusing. The documentation for the FeatureDefinition class and EnabledFor do not provide any useful information.

Does anyone know how to use Microsoft.FeatureManagement in .NET Core 3.1 to extract feature data from the database?

like image 423
Phillip Copley Avatar asked Nov 19 '20 12:11

Phillip Copley


People also ask

How do I open feature selection in SQL Server?

To add a new feature to an existing instance of SQL Server, select Installation in the left-hand navigation area, and then select New SQL Server stand-alone installation or add features to an existing installation. The System Configuration Checker will run a discovery operation on your computer.


1 Answers

You are right, the examples and tutorials are unbelievably shallow, confusing and don't clarify any of the concepts. However I couldn't help but notice that the example suggested above only deals with configuration settings, JSON, etc. but it would be hard to use for your purpose, i.e., querying SQL Server and returning the state of the feature. What you need is an example of how to implement the interface IFeatureDefinitionProvider and be able to query the SQL Server database in a flexible way. This is the way I would do it and not necessarily using the Entity Framework, ADO.NET is enough:

namespace FeatureDefinitionProviderDemo
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Microsoft.FeatureManagement;

    public class FeatureDefinitionProvider : IFeatureDefinitionProvider
    {
        private const string FirstFeatureName = "FirstFeature";
        private const string SecondFeatureName = "SecondFeature";
        private const string ThirdFeatureName = "ThirdFeature";

        public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
        {
            // YOU SUPPOSEDLY GO ASYNCHRONOUSLY TO THE DATABASE AND TAKE THE GIVEN FEATURE PROPERTIES
            // NOTE: part of the following is dummy
            var featureDefinition = featureName switch // NOTE: I'm using new C# switch expression here
            {
                // let's say the feature is boolean and it is enabled
                FirstFeatureName => CreateEnabledFeatureDefinition(featureName),

                // let's say the feature is boolean and it is disabled
                SecondFeatureName => CreateDisabledFeatureDefinition(featureName),

                // let's say this one is a 50% percentage
                ThirdFeatureName => CreatePercentageFeatureDefinition(featureName, 50),

                _ => throw new NotSupportedException("The requested feature is not supported.")
            };

            return Task.FromResult(featureDefinition);
        }

        public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
        {
            foreach (var featureDefinition in new[]
            {
                await GetFeatureDefinitionAsync(FirstFeatureName),
                await GetFeatureDefinitionAsync(SecondFeatureName),
                await GetFeatureDefinitionAsync(ThirdFeatureName),
            })
            {
                yield return featureDefinition;
            }
        }

        private FeatureDefinition CreateEnabledFeatureDefinition(string featureName)
        {
            // NOTE: adding a filter configuration without configurations means enabled
            return new FeatureDefinition
            {
                Name = featureName,
                EnabledFor = new[]
                {
                    new FeatureFilterConfiguration
                    {
                        Name = "AlwaysOn"
                    }
                }
            };
        }

        private FeatureDefinition CreateDisabledFeatureDefinition(string featureName)
        {
            // NOTE: don't add any filter configuration as by default it is disabled
            return new FeatureDefinition
            {
                Name = featureName
            };
        }

        private FeatureDefinition CreatePercentageFeatureDefinition(string featureName, double percentage)
        {
            // NOTE: this one is a bit more complicated and could be connected to a percentage SQL server setting
            return new FeatureDefinition
            {
                Name = featureName,
                EnabledFor = new[]
                {
                    new FeatureFilterConfiguration
                    {
                        Name = "Percentage",
                        Parameters = new DoubleConfiguration(percentage)
                    }
                }
            };
        }
    }
}

namespace FeatureDefinitionProviderDemo
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Primitives;

    internal class DoubleConfiguration : IConfiguration
    {
        private readonly double _percentage;

        public DoubleConfiguration(double percentage)
        {
            _percentage = percentage;
        }

        public IEnumerable<IConfigurationSection> GetChildren()
        {
            // NOTE: no children
            return Enumerable.Empty<IConfigurationSection>();
        }

        public IChangeToken GetReloadToken()
        {
            // NOTE: this is not supported and not consumed either
            throw new NotSupportedException();
        }

        public IConfigurationSection GetSection(string key)
        {
            // NOTE: this is not supported and not consumed either
            throw new NotSupportedException();
        }

        public string this[string key]
        {
            get => _percentage.ToString("F"); // this produces the requested value

            // NOTE: this is not supported and not consumed either
            set => throw new NotSupportedException();
        }
    }
}
like image 63
Lucky Brain Avatar answered Sep 20 '22 05:09

Lucky Brain