-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* init * draft * use ValueTask * support factory method * add example * update * Update * Update * update example * match variant name or configuration value * update to the latest design * merge with preview branch * resolve comments * rename to VariantService * update & add comments * remove POC example * add testcases & use method name GetServiceAsync * update comments * add variant service cache * resolve comments * throw exception for duplicated registration * add testcase * remove unused package * update comment * set feature name in constructor
- Loading branch information
1 parent
8906633
commit 613380b
Showing
8 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
src/Microsoft.FeatureManagement/IVariantServiceProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
// | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.FeatureManagement | ||
{ | ||
/// <summary> | ||
/// Used to get different implementation variants of TService. | ||
/// </summary> | ||
public interface IVariantServiceProvider<TService> where TService : class | ||
{ | ||
/// <summary> | ||
/// Gets an implementation variant of TService. | ||
/// </summary> | ||
/// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||
/// <returns>An implementation of TService.</returns> | ||
ValueTask<TService> GetServiceAsync(CancellationToken cancellationToken); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Microsoft.FeatureManagement/VariantServiceAliasAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
// | ||
using System; | ||
|
||
namespace Microsoft.FeatureManagement | ||
{ | ||
/// <summary> | ||
/// Allows the name of a variant service to be customized to relate to the variant name specified in configuration. | ||
/// </summary> | ||
public class VariantServiceAliasAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// Creates a variant service alias using the provided alias. | ||
/// </summary> | ||
/// <param name="alias">The alias of the variant service.</param> | ||
public VariantServiceAliasAttribute(string alias) | ||
{ | ||
if (string.IsNullOrEmpty(alias)) | ||
{ | ||
throw new ArgumentNullException(nameof(alias)); | ||
} | ||
|
||
Alias = alias; | ||
} | ||
|
||
/// <summary> | ||
/// The name that will be used to match variant name specified in the configuration. | ||
/// </summary> | ||
public string Alias { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
// | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.FeatureManagement | ||
{ | ||
/// <summary> | ||
/// Used to get different implementations of TService depending on the assigned variant from a specific variant feature flag. | ||
/// </summary> | ||
internal class VariantServiceProvider<TService> : IVariantServiceProvider<TService> where TService : class | ||
{ | ||
private readonly IEnumerable<TService> _services; | ||
private readonly IVariantFeatureManager _featureManager; | ||
private readonly string _featureName; | ||
private readonly ConcurrentDictionary<string, TService> _variantServiceCache; | ||
|
||
/// <summary> | ||
/// Creates a variant service provider. | ||
/// </summary> | ||
/// <param name="featureName">The feature flag that should be used to determine which variant of the service should be used.</param> | ||
/// <param name="featureManager">The feature manager to get the assigned variant of the feature flag.</param> | ||
/// <param name="services">Implementation variants of TService.</param> | ||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="featureName"/> is null.</exception> | ||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="featureManager"/> is null.</exception> | ||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="services"/> is null.</exception> | ||
public VariantServiceProvider(string featureName, IVariantFeatureManager featureManager, IEnumerable<TService> services) | ||
{ | ||
_featureName = featureName ?? throw new ArgumentNullException(nameof(featureName)); | ||
_featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager)); | ||
_services = services ?? throw new ArgumentNullException(nameof(services)); | ||
_variantServiceCache = new ConcurrentDictionary<string, TService>(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets implementation of TService according to the assigned variant from the feature flag. | ||
/// </summary> | ||
/// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||
/// <returns>An implementation matched with the assigned variant. If there is no matched implementation, it will return null.</returns> | ||
public async ValueTask<TService> GetServiceAsync(CancellationToken cancellationToken) | ||
{ | ||
Debug.Assert(_featureName != null); | ||
|
||
Variant variant = await _featureManager.GetVariantAsync(_featureName, cancellationToken); | ||
|
||
TService implementation = null; | ||
|
||
if (variant != null) | ||
{ | ||
implementation = _variantServiceCache.GetOrAdd( | ||
variant.Name, | ||
(_) => _services.FirstOrDefault( | ||
service => IsMatchingVariantName( | ||
service.GetType(), | ||
variant.Name)) | ||
); | ||
} | ||
|
||
return implementation; | ||
} | ||
|
||
private bool IsMatchingVariantName(Type implementationType, string variantName) | ||
{ | ||
string implementationName = ((VariantServiceAliasAttribute)Attribute.GetCustomAttribute(implementationType, typeof(VariantServiceAliasAttribute)))?.Alias; | ||
|
||
if (implementationName == null) | ||
{ | ||
implementationName = implementationType.Name; | ||
} | ||
|
||
return string.Equals(implementationName, variantName, StringComparison.OrdinalIgnoreCase); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Microsoft.FeatureManagement; | ||
|
||
namespace Tests.FeatureManagement | ||
{ | ||
interface IAlgorithm | ||
{ | ||
public string Style { get; } | ||
} | ||
|
||
class AlgorithmBeta : IAlgorithm | ||
{ | ||
public string Style { get; set; } | ||
|
||
public AlgorithmBeta() | ||
{ | ||
Style = "Beta"; | ||
} | ||
} | ||
|
||
class AlgorithmSigma : IAlgorithm | ||
{ | ||
public string Style { get; set; } | ||
|
||
public AlgorithmSigma() | ||
{ | ||
Style = "Sigma"; | ||
} | ||
} | ||
|
||
[VariantServiceAlias("Omega")] | ||
class AlgorithmOmega : IAlgorithm | ||
{ | ||
public string Style { get; set; } | ||
|
||
public AlgorithmOmega(string style) | ||
{ | ||
Style = style; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters