dotnet-sdk icon indicating copy to clipboard operation
dotnet-sdk copied to clipboard

OpenFeature.Hosting doesn't target netstandard or framework

Open kylejuliandev opened this issue 7 months ago • 4 comments

The OpenFeature.Hosting project doesn't target the same frameworks as OpenFeature.DependencyInjection, which means users in .NET Framework cannot easily get started with the dependency injection initialization approach.

https://github.com/open-feature/dotnet-sdk/blob/48f57cd40c2754bbbb08202f338e2452522fe691/src/OpenFeature.Hosting/OpenFeature.Hosting.csproj#L3-L8

OpenFeature.Hosting only depends on Microsoft.Extensions.Hosting.Abstractions which does have support for .NET Framework 4.6.2 and .NET Standard 2.0.

kylejuliandev avatar May 17 '25 12:05 kylejuliandev

I wonder if we should kill Hosting and move this logic to the DependencyInjection library.

Also, I am thinking of doing this

builder.Services.AddOpenFeature(featureBuilder => {
    featureBuilder
        .AddHostedFeatureLifecycle() // Remove from explicit call to the `AddOpenFeature`.
        .AddInMemoryProvider();
});

That would translate into a much cleaner initialisation and easier onboarding.

builder.Services.AddOpenFeature(featureBuilder => {
    featureBuilder
        .AddInMemoryProvider();
});

Opinions @kylejuliandev and @arttonoyan? Is it possible to just call AddHostedFeatureLifecycle() when it is needed?

askpt avatar May 19 '25 16:05 askpt

From my own experiences, when I've setup relatively simple applications (like a minimal api in .NET 9) I have forgotton on multiple occasions to import both NuGet packages. When you forgot, the issue doesn't immediately manifest. But rather you have to investigate why a certain feature flag is only returning the default variant. You may have imported both packages but forgot to call AddHostedFeatureLifecycle() on the OpenFeatureBuilder.

AddHostedFeatureLifecycle() does feel like it should implicitly happen when you make a call to builder.Services.AddOpenFeature().

I'd like to understand the rationale behind the decision of having two seperate packages and an additional mandatory (not obvious) builder extension method.

kylejuliandev avatar May 19 '25 20:05 kylejuliandev

Hey @kylejuliandev, there was quite a bit of discussion around this when we first added DI. Here's the original PR. If I remember correctly, we wanted to keep it in a separate package until we were happy with the behavior. Now that it has been live for a while, we could consider including it in the SDK and dropping the experimental label.

FYI @toddbaert @arttonoyan

beeme1mr avatar May 20 '25 18:05 beeme1mr

@kylejuliandev Thanks for the thoughtful feedback - it's a great observation.

The reason we keep AddHostedFeatureLifecycle() in a separate package is to support flexibility across different application types. The Hosted and DependencyInjection packages serve distinct purposes. While AddOpenFeature() is designed for registering core services, not every application requires hosted infrastructure. For instance, desktop apps like Windows Forms or WPF, or lightweight console apps, may use DI but don't need lifecycle management via IHostedService.

This separation also aligns with Microsoft’s own approach — where hosting, diagnostics, and core service registration are provided via separate packages — giving developers fine-grained control over dependencies and application structure.

That said, I completely agree the current developer experience could be more intuitive. It's easy to forget AddHostedFeatureLifecycle(), and the resulting fallback to the default variant is hard to trace.

Maybe we can provide a new extension method in the Hosted package, something like .AddOpenFeatureServer() (or another appropriate name), which would internally call AddOpenFeature(), AddHostedFeatureLifecycle(), and register any other host-specific services. This could offer a smoother setup experience for server-based apps without sacrificing modularity. cc: @beeme1mr @toddbaert @askpt

arttonoyan avatar May 20 '25 20:05 arttonoyan

@arttonoyan and @kylejuliandev, I was playing around with Copilot to generate some documentation for issue #471. I think it was a great suggestion in relation to the AddHostedLifecycle issue we have.

What are your thoughts about using

using Microsoft.Extensions.Hosting;
using OpenFeature.Hosting;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureOpenFeature(builder =>
    {
        builder.AddProvider(new MyFeatureFlagProvider());
    })
    .Build();

await host.RunAsync();

I know the file is completely wrong, but I liked the suggestions. See it here: https://github.com/open-feature/dotnet-sdk/blob/bbc77bfed182d35d027aff68fc1419f74b05e0e9/src/OpenFeature.Hosting/README.md

askpt avatar May 28 '25 16:05 askpt

I wonder if we should kill Hosting and move this logic to the DependencyInjection library.

Think combine them into one library could help reduce the integration complexity.

OpenTelemetry is adopting the OpenTelemetry.Extensions.Hosting which has a dependency for Microsoft.Extensions.Hosting.Abstractions

WeihanLi avatar Jun 03 '25 11:06 WeihanLi

@askpt and @kylejuliandev I've been reflecting on this topic and exploring how other frameworks handle similar scenarios. Previously, I advocated for maintaining two separate packages: OpenFeature.DependencyInjection and OpenFeature.Hosting, to provide flexibility and modularity. However, upon further consideration and observing industry trends, I believe it's worth reevaluating this approach.

A notable example is the MassTransit framework. Starting with version 8, MassTransit deprecated the AddMassTransitHostedService() method. Previously, developers had to explicitly register the hosted service to manage the bus lifecycle, which often led to misconfigurations when this step was overlooked. To address this, MassTransit integrated the hosted service registration directly into the AddMassTransit() method, streamlining the configuration process and reducing potential errors.

Given that most applications utilizing OpenFeature will require hosting capabilities, adopting a similar approach could enhance the developer experience and simplify setup. I support the idea of integrating hosting configuration into the main package. Additionally, providing an option to customize hosting behavior, akin to MassTransit's use of MassTransitHostOptions, would offer flexibility for advanced scenarios.

Here are some relevant references for further context:

arttonoyan avatar Jun 03 '25 20:06 arttonoyan

@askpt and @kylejuliandev I've been reflecting on this topic and exploring how other frameworks handle similar scenarios. Previously, I advocated for maintaining two separate packages: OpenFeature.DependencyInjection and OpenFeature.Hosting, to provide flexibility and modularity. However, upon further consideration and observing industry trends, I believe it's worth reevaluating this approach.

A notable example is the MassTransit framework. Starting with version 8, MassTransit deprecated the AddMassTransitHostedService() method. Previously, developers had to explicitly register the hosted service to manage the bus lifecycle, which often led to misconfigurations when this step was overlooked. To address this, MassTransit integrated the hosted service registration directly into the AddMassTransit() method, streamlining the configuration process and reducing potential errors.

Given that most applications utilizing OpenFeature will require hosting capabilities, adopting a similar approach could enhance the developer experience and simplify setup. I support the idea of integrating hosting configuration into the main package. Additionally, providing an option to customize hosting behavior, akin to MassTransit's use of MassTransitHostOptions, would offer flexibility for advanced scenarios.

Here are some relevant references for further context:

@arttonoyan Thank you for your updated analysis. I think it follows the proposal that @kylejuliandev had. I vote that we merge both libraries and remove that .AddHosted...(). If no one has further blockers, I think we can proceed. We will need to come up with a plan to deprecate one library and likely a migration plan. Asking now @open-feature/sdk-dotnet-approvers and @open-feature/sdk-dotnet-maintainers for more opinions on this topic.

askpt avatar Jun 04 '25 18:06 askpt

I vote that we merge both libraries and remove that .AddHosted...(). If no one has further blockers, I think we can proceed. We will need to come up with a plan to deprecate one library and likely a migration plan.

Any plan to move forward?

WeihanLi avatar Jun 17 '25 03:06 WeihanLi

@WeihanLi, @kylejuliandev, @arttonoyan Let's follow this recommendation: https://github.com/open-feature/dotnet-sdk/issues/472#issuecomment-2937082094

We should keep the DI package and deprecate the Hosting one. Let's also make sure the AddHosting is no longer required. The migration plan is:

  • [ ] Add Deprecation annotation to the AddHosting and add note to the README
  • [ ] Move the code to the DI package and do the hosting registration behind the scenes
  • [ ] Show deprecation notice on Nuget package
  • [ ] Remove old code but keeping a readme at the root of the hosting project with instructions on the migration

At the end, we should be able to only declare the DI package and have everything registered correctly. @kylejuliandev if you are okay with it, can we change the title and description of this issue?

askpt avatar Jul 03 '25 20:07 askpt

Sounds good to me 👍 @askpt

kylejuliandev avatar Jul 03 '25 20:07 kylejuliandev