Skip to content

Conversation

@arttonoyan
Copy link
Contributor

@arttonoyan arttonoyan commented Oct 4, 2025

Important

Update. 2025-11-10
Following the discussion in #596 (comment). the package name in this proposal is updated from OpenFeature.DependencyInjection to OpenFeature.Providers.DependencyInjection. Rationale. clearer scope for provider authors. thinner provider packages. reduced coupling with the SDK.

This PR

Extract the minimal, provider-agnostic DI surface into a new package: OpenFeature.DependencyInjection.Abstractions.
This isolates the contracts and lightweight wiring needed to integrate any OpenFeature provider without pulling in a concrete implementation.

Related Issues

Fixes #587

Notes

  • Options split: Extracted provider-agnostic configuration from OpenFeatureOptions into a new base options type: OpenFeatureProviderOptions.
  • Inheritance: OpenFeatureOptions now inherits from OpenFeatureProviderOptions.
  • Internal wiring update: Internal configuration that previously targeted OpenFeatureOptions now targets OpenFeatureProviderOptions.

Before (internal)

services.AddOptions<OpenFeatureOptions>()
    .Configure(options =>
    {
        options.AddProviderName(null);
    });

After (internal)

services.AddOptions<OpenFeatureProviderOptions>()
    .Configure(options =>
    {
        options.AddProviderName(null);
    });

Note: This is an internal refactor. It does not affect consumers unless they were directly modifying OpenFeatureOptions via DI options configuration (which is not a supported/typical usage).


Impact

  • No new functionality.
  • Behavior unchanged at runtime (same provider selection, same evaluation outcomes, same logs).
  • Binary/source compatibility: Consumers using the documented extension methods and standard registration remain unaffected.
  • Potential touchpoints: Only internal code paths (or unconventional consumer code directly configuring OpenFeatureOptions) require updates to target OpenFeatureProviderOptions.

How to test

This is a regression-only refactor.

Expectations

  • All existing unit/integration tests pass unchanged.
  • No DI resolution or startup configuration differences.

Introduced a new project, `OpenFeature.DependencyInjection.Abstractions`, to the solution. This project supports dependency injection abstractions and targets multiple frameworks (`netstandard2.0`, `net8.0`, `net9.0`, `net462`) for broad compatibility.

Configured the project with the `Microsoft.NET.Sdk` SDK and set the root namespace to `OpenFeature.DependencyInjection.Abstractions`. Added dependencies on `Microsoft.Extensions.DependencyInjection.Abstractions` and `Microsoft.Extensions.Options` to enable DI and options configuration.
Refactored the OpenFeature framework to introduce `OpenFeatureProviderBuilder`, enhancing support for dependency injection and provider management.

- Changed namespaces to align with DI abstractions.
- Made `FeatureCodes` public for broader accessibility.
- Added `InternalsVisibleTo` for testing and project references.
- Introduced `OpenFeatureProviderBuilder` for managing providers and policies.
- Added extension methods for provider and policy registration.
- Refactored `OpenFeatureBuilder` to inherit from `OpenFeatureProviderBuilder`.
- Consolidated shared functionality in `OpenFeatureProviderOptions`.
- Updated `FeatureBuilderExtensions` and `InMemoryProviderOptions` to use the new abstraction.
- Updated tests to reflect new method signatures and hierarchy.
- Removed redundant methods and properties, improving code organization.

These changes improve maintainability, extensibility, and alignment with modern DI patterns.
Simplified the `AddProvider` API by removing the `TFeatureProvider` generic type parameter and directly using the `FeatureProvider` type. Updated the `TOptions` parameter to ensure it derives from `OpenFeatureProviderOptions`.

Added a `<ProjectReference>` to `OpenFeature.csproj` in `OpenFeature.DependencyInjection.Abstractions.csproj`. Updated `OpenFeatureOptions` to `OpenFeatureProviderOptions` in the default name selector policy.

Refactored `AddInMemoryProvider` methods to align with the new API. Updated tests in `OpenFeatureBuilderExtensionsTests` to reflect the changes and validate the updated functionality.

Performed general code cleanup, including XML documentation updates, to improve clarity and maintain consistency across the codebase.
Refactored `FeatureLifecycleManager` to modularize provider, hook, and handler initialization with new methods: `InitializeProvidersAsync`, `InitializeHooks`, and `InitializeHandlers`. Updated `EnsureInitializedAsync` to use these methods for improved readability and maintainability.

Revised `AddInMemoryProvider` in `FeatureBuilderExtensions` to use a generic `FeatureProvider` abstraction. Adjusted `CreateProvider` methods accordingly.

Improved code clarity in `FeatureFlagIntegrationTest` by renaming variables for consistency and removing redundant assignments.
Updated FeatureLifecycleManagerTests to replace OpenFeatureOptions
with OpenFeatureProviderOptions for configuring feature providers.
Added support for hooks and keyed singletons to enhance modularity.
Introduced additional feature flag retrieval in FeatureFlagIntegrationTest.
Added dependency on OpenFeature.DependencyInjection.Abstractions.
@codecov
Copy link

codecov bot commented Oct 4, 2025

Codecov Report

❌ Patch coverage is 95.55556% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.91%. Comparing base (ddd8f22) to head (579ac49).

Files with missing lines Patch % Lines
...yInjection/OpenFeatureProviderBuilderExtensions.cs 90.24% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #596      +/-   ##
==========================================
- Coverage   89.99%   89.91%   -0.09%     
==========================================
  Files          77       80       +3     
  Lines        3198     3212      +14     
  Branches      368      373       +5     
==========================================
+ Hits         2878     2888      +10     
  Misses        251      251              
- Partials       69       73       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@askpt askpt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I only added a couple of minor comments.

Please have a chat with @kylejuliandev to try understand the impact on his deprecations efforts. I remember he mentioned a couple of fixes he was working on for the DI and I don't want that effort to get lost during this PR.

}

/// <inheritdoc />
private async Task InitializeProvidersAsync(CancellationToken cancellationToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancellationToken seems to not be used here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had always assumed SetProviderAsync would have a cancellationToken overload, but it doesn't appear like it does

/// <summary>
/// Sets the default feature provider. In order to wait for the provider to be set, and initialization to complete,
/// await the returned task.
/// </summary>
/// <remarks>The provider cannot be set to null. Attempting to set the provider to null has no effect. May throw an exception if <paramref name="featureProvider"/> cannot be initialized.</remarks>
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
public async Task SetProviderAsync(FeatureProvider featureProvider)
{
this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider);
await this._repository.SetProviderAsync(featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false);
}
/// <summary>
/// Binds the feature provider to the given domain. In order to wait for the provider to be set, and
/// initialization to complete, await the returned task.
/// </summary>
/// <remarks>The provider cannot be set to null. Attempting to set the provider to null has no effect. May throw an exception if <paramref name="featureProvider"/> cannot be initialized.</remarks>
/// <param name="domain">An identifier which logically binds clients with providers</param>
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
/// <exception cref="ArgumentNullException">domain cannot be null or empty</exception>
public async Task SetProviderAsync(string domain, FeatureProvider featureProvider)
{
if (string.IsNullOrWhiteSpace(domain))
{
throw new ArgumentNullException(nameof(domain));
}
this._eventExecutor.RegisterClientFeatureProvider(domain, featureProvider);
await this._repository.SetProviderAsync(domain, featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false);
}

arttonoyan and others added 3 commits November 5, 2025 23:47
Refactored `OpenFeatureOptions` to `OpenFeatureProviderOptions`
to improve clarity and maintainability. Updated all references
and test cases to use the new class. Removed inheritance of
`OpenFeatureOptions` from `OpenFeatureProviderOptions` and
introduced `_hookNames` for managing hook names.

Replaced `TestOptions` with `TestProviderOptions` in
`OpenFeatureBuilderExtensionsTests`, adding a `SomeFlag`
property for more flexible testing. Renamed and updated
`OpenFeatureOptionsTests` to `OpenFeatureProviderOptionsTests`.

Performed general cleanup, including removing unused
namespaces, ensuring consistent naming conventions, and
aligning method calls with the new class structure.
Migrated `OpenFeature.DependencyInjection.Abstractions` to a new project, `OpenFeature.Providers.DependencyInjection`, to improve modularity and maintainability. Updated namespaces, project references, and `using` directives accordingly.

Removed the deprecated `OpenFeature.DependencyInjection.Abstractions` project and its associated files. Introduced `OpenFeature.Providers.DependencyInjection.csproj` to house the migrated functionality.

Updated test files to reference the new namespace and adjusted experimental feature annotations. Reorganized build configuration files to align with the new project structure. Ensured documentation and metadata reflect the refactored codebase.
@arttonoyan
Copy link
Contributor Author

arttonoyan commented Nov 6, 2025

@askpt @kylejuliandev I’ve made all the requested changes. I only left out the README.md. If that’s OK, I’ll handle it in a separate ticket. Please review again.

Quick question: Should I apply the same changes to the DI package, or skip it because it’s obsolete?
Need Help: I can’t figure out the dotnet format / check-format job error. Could you help with this?

@askpt
Copy link
Member

askpt commented Nov 7, 2025

@askpt @kylejuliandev I’ve made all the requested changes. I only left out the README.md. If that’s OK, I’ll handle it in a separate ticket. Please review again.

Quick question: Should I apply the same changes to the DI package, or skip it because it’s obsolete? Need Help: I can’t figure out the dotnet format / check-format job error. Could you help with this?

@arttonoyan just execute dotnet format OpenFeature.slnx at the root of the repository to clear the error. You also have a DCO failure.

Removed `OpenFeature.Hosting.Internal` namespace from
`FeatureLifecycleManagerTests.cs` and
`OpenFeatureBuilderExtensionsTests.cs` as it is no longer
required. Adjusted the placement of
`OpenFeature.Providers.DependencyInjection` for consistency
and improved readability. These changes streamline imports
and enhance maintainability.
@arttonoyan arttonoyan changed the title feat: Extract minimal DI integration to OpenFeature.DependencyInjection.Abstractions feat: Extract minimal DI integration to OpenFeature.Providers.DependencyInjection Nov 10, 2025
@arttonoyan
Copy link
Contributor Author

@askpt @kylejuliandev Could you please do a final review? I’d like to merge this PR.

@askpt
Copy link
Member

askpt commented Nov 11, 2025

@arttonoyan Please update this action: https://github.com/open-feature/dotnet-sdk/blob/main/.github/workflows/release.yml. This package needs to be added to generate the security artefacts.

Signed-off-by: Artyom Tonoyan <[email protected]>
@arttonoyan
Copy link
Contributor Author

@arttonoyan Please update this action: https://github.com/open-feature/dotnet-sdk/blob/main/.github/workflows/release.yml. This package needs to be added to generate the security artefacts.

done: 579ac49

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Minimal OpenFeature.Providers.DependencyInjection with Builder API (Hosting retains DI registrations)

4 participants