microsoft / microsoft/FeatureManagement-Dotnet
How to Implement Hierarchical Feature Dependencies Without Circular DI?
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 1.2k
- Forks
- 129
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 5
Description
Context
I'm implementing hierarchical feature dependencies where one feature can depend on other features being enabled. I'm using a database-backed provider (similar to PR #389) with AddScopedFeatureManagement().
The Scenario
I want to implement a FeatureDependency filter that allows one feature to depend on others:
{
"id": "Dashboard",
"enabled": true,
"conditions": {
"requirement_type": "Any",
"client_filters": [
{
"name": "FeatureDependency",
"parameters": { "FeatureName": "BasicReporting" }
},
{
"name": "FeatureDependency",
"parameters": { "FeatureName": "AdvancedReporting" }
}
]
}
}
The Dashboard feature should be enabled if either BasicReporting OR AdvancedReporting is enabled.
These dependent features themselves have their own conditions:
{
"id": "BasicReporting",
"enabled": true,
"conditions": {
"client_filters": [
{
"name": "TimeWindow",
"parameters": { "Start": "...", "End": "..." }
}
]
}
}
{
"id": "AdvancedReporting",
"enabled": true,
"conditions": {
"requirement_type": "All",
"client_filters": [
{
"name": "Percentage",
"parameters": { "Value": 50 }
},
{
"name": "FeatureDependency",
"parameters": { "FeatureName": "PremiumSubscription" }
}
]
}
}
To properly evaluate Dashboard, the FeatureDependency filter needs to evaluate BasicReporting and AdvancedReporting through their complete filter pipeline (TimeWindow, Percentage, nested dependencies, etc.). This requires calling IFeatureManager.IsEnabledAsync().
What I Tried
I implemented the filter like this:
[FilterAlias("FeatureDependency")]
public class FeatureDependencyFilter : IFeatureFilter
{
private readonly IFeatureManager _featureManager;
public FeatureDependencyFilter(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
{
string? featureName = context.Parameters.GetValue<string>("FeatureName");
if (string.IsNullOrEmpty(featureName))
{
return false;
}
return await _featureManager.IsEnabledAsync(featureName);
}
}
Registered with:
services.AddScopedFeatureManagement()
.AddFeatureFilter<FeatureDependencyFilter>();
The Problem
This creates a circular dependency during DI container construction:
IFeatureManager (being created)
→ needs IFeatureFilterMetadata[] (including FeatureDependencyFilter)
→ FeatureDependencyFilter constructor needs IFeatureManager
→ Circular dependency!
Result: The application hangs indefinitely when trying to resolve IFeatureManager:
using (IServiceScope scope = serviceProvider.CreateScope())
{
IFeatureManager featureManager = scope.ServiceProvider.GetRequiredService<IFeatureManager>();
}
The Solution I Arrived At
Breaking the circular dependency by resolving IFeatureManager at evaluation time instead of in the constructor:
[FilterAlias("FeatureDependency")]
public class FeatureDependencyFilter : IFeatureFilter
{
private readonly IServiceProvider _serviceProvider;
public FeatureDependencyFilter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
{
string? featureName = context.Parameters.GetValue<string>("FeatureName");
if (string.IsNullOrEmpty(featureName))
{
return false;
}
IFeatureManager featureManager = _serviceProvider.GetRequiredService<IFeatureManager>();
return await featureManager.IsEnabledAsync(featureName);
}
}
Questions
- Is this the recommended pattern for implementing hierarchical feature dependencies?
- Is there a built-in way to express feature dependencies without creating a custom filter?
- Is using
IServiceProviderdirectly the correct approach to break the circular dependency, or is there a better pattern? - Does the default configuration-based provider handle cycle detection? If feature dependencies form a cycle (e.g., FeatureA → FeatureB → FeatureA), does the library have built-in safeguards, or should I implement my own cycle detection?
Any guidance on the proper pattern for hierarchical feature dependencies would be greatly appreciated!
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the AddScopedFeatureManagement registration and the IFeatureManager/IFeatureFilter interaction shown in the issue. The payload names no repository files or tests; work is not bounded until maintainers clarify whether the desired outcome is a built-in dependency mechanism, guidance on deferred resolution, or cycle detection.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100