{"id":18463923,"url":"https://github.com/unleash/unleash-client-dotnet","last_synced_at":"2025-05-15T09:00:41.400Z","repository":{"id":38047195,"uuid":"112716413","full_name":"Unleash/unleash-client-dotnet","owner":"Unleash","description":"Unleash client SDK for .NET","archived":false,"fork":false,"pushed_at":"2025-03-10T14:05:42.000Z","size":2157,"stargazers_count":85,"open_issues_count":8,"forks_count":41,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-01T03:24:07.828Z","etag":null,"topics":["dotnet","feature-toggling"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Unleash.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-01T08:45:42.000Z","updated_at":"2025-03-10T14:05:21.000Z","dependencies_parsed_at":"2023-10-17T00:05:19.497Z","dependency_job_id":"8ef0b51f-6dbd-4f5c-b8c7-b67e4048320f","html_url":"https://github.com/Unleash/unleash-client-dotnet","commit_stats":null,"previous_names":[],"tags_count":84,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Unleash%2Funleash-client-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Unleash%2Funleash-client-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Unleash%2Funleash-client-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Unleash%2Funleash-client-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Unleash","download_url":"https://codeload.github.com/Unleash/unleash-client-dotnet/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247773711,"owners_count":20993634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet","feature-toggling"],"created_at":"2024-11-06T09:08:22.148Z","updated_at":"2025-04-08T04:09:40.097Z","avatar_url":"https://github.com/Unleash.png","language":"C#","readme":"# Unleash FeatureToggle Client for .Net\n\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](code-of-conduct.md)\n[![NuGet](https://img.shields.io/nuget/v/Unleash.Client.svg)](https://www.nuget.org/packages/Unleash.Client/)\n\n\n\u003e  **Migrating to v5**\n\u003e\n\u003e If you use [bootstrapping](#bootstrapping), [custom strategies](#custom-strategies), or a custom JSON serializer, read the complete [migration guide](./v5_MIGRATION_GUIDE.md) before upgrading to v5.\n\n\n## Introduction\n\nUnleash Client SDK for .Net. It is compatible with the\n[Unleash-hosted.com SaaS offering](https://www.unleash-hosted.com/) and\n[Unleash Open-Source](https://github.com/unleash/unleash).\n\nThe main motivation for doing feature toggling is to decouple the process for deploying code to production and releasing new features. This helps reducing risk, and allow us to easily manage which features to enable.\n\nFeature toggles decouple deployment of code from release of new features.\n\nTake a look at the demonstration site at [unleash.herokuapp.com](http://unleash.herokuapp.com/)\n\nRead more of the main project at [github.com/unleash/unleash](https://github.com/Unleash/unleash)\n\n## Features\nSupported Frameworks\n* .Net 6\n* NET Standard 2.0\n* .Net 4.8\n* .Net 4.7.2\n* .Net 4.7\n* .Net 4.6.1\n* .Net 4.6\n* .Net 4.5.1\n* .Net 4.5\n\nExtendable architecture\n- Inject your own implementations of key components (background task scheduler, http client factory)\n\n## Getting started\n\n### Install the package\n\nInstall the latest version of `Unleash.Client` from [nuget.org](https://www.nuget.org/packages/Unleash.Client/) or use the `dotnet` cli:\n\n``` bash\ndotnet add package unleash.client\n```\n\n### Create a new a Unleash instance\n\n---\n\n**⚠️ Important:** In almost every case, you only want a **single, shared instance of the Unleash  client (a *singleton*)** in your application . You would typically use a dependency injection framework to inject it where you need it. Having multiple instances of the client in your application could lead to inconsistencies and performance degradation.\n\nIf you create more than 10 instances, Unleash will attempt to log warnings about your usage.\n\n---\n\nTo create a new instance of Unleash you need to create and pass in an `UnleashSettings` object.\n\nWhen creating an instance of the Unleash client, you can choose to do it either **synchronously** or **asynchronously**.\nThe SDK will synchronize with the Unleash API on initialization, so it can take a moment for the it to reach the correct state. With an asynchronous startup, this would happen in the background while the rest of your code keeps executing. In most cases, this isn't an issue. But if you want to **wait until the SDK is fully synchronized**, then you should use the configuration explained in the [synchronous startup](#synchronous-startup) section.\nThis is usually not an issue and Unleash will do this in the background as soon as you initialize it.\nHowever, if it's important that you do not continue execution until the SDK has synchronized, then you should use the configuration explained in the [synchronous startup](#synchronous-startup) section.\n\n```csharp\nusing Unleash;\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"\u003cyour-api-url\u003e\"),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n      {\"Authorization\",\"\u003cyour-api-token\u003e\" }\n    }\n};\n\nvar unleash = new DefaultUnleash(settings);\n\n// Add to Container as Singleton\n// .NET Core 3/.NET 5/...\nservices.AddSingleton\u003cIUnleash\u003e(c =\u003e unleash);\n\n```\n\nWhen your application shuts down, remember to dispose the unleash instance.\n\n```csharp\nunleash?.Dispose()\n```\n\n#### Synchronous startup\n\nThis unleash client does not throw any exceptions if the unleash server is unreachable. Also, fetching features will return the default value if the feature toggle cache has not yet been populated. In many situations it is perferable to throw an error than allow an application to startup with incorrect feature toggle values. For these cases, we provide a client factory with the option for synchronous initialization.\n\n```csharp\nusing Unleash;\nusing Unleash.ClientFactory;\n\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"\u003cyour-api-url\u003e\"),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n       {\"Authorization\",\"\u003cyour-api-token\u003e\" }\n    }\n};\nvar unleashFactory = new UnleashClientFactory();\n\nIUnleash unleash = await unleashFactory.CreateClientAsync(settings, synchronousInitialization: true);\n\n// this `unleash` has successfully fetched feature toggles and written them to its cache.\n// if network errors or disk permissions prevented this from happening, the above await would have thrown an exception\n\nvar awesome = unleash.IsEnabled(\"SuperAwesomeFeature\");\n```\n\nThe `CreateClientAsync` method was introduced in version 1.5.0, making the previous `Generate` method obsolete. There's also a `CreateClient` method available if you don't prefer the async version.\n\n\n#### Project-scoped Unleash client\n\nIf you're organizing your feature toggles in projects in Unleash Enterprise, you can [scope your API tokens](https://docs.getunleash.io/how-to/how-to-create-project-api-tokens) to include only the specific projects needed for your client. Then use that token when configuring the Unleash client:\n\n```csharp\n\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\"),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n       {\"Authorization\",\"\u003cyour-project-scoped-api-token\u003e\" }\n    }\n};\n\n```\n\n### Check feature toggles\n\nThe `IsEnabled` method allows you to check whether a feature is enabled:\n\n```csharp\nif(unleash.IsEnabled(\"SuperAwesomeFeature\"))\n{\n  //do some magic\n}\nelse\n{\n  //do old boring stuff\n}\n```\n\nIf the Unleash client can't find the feature you're trying to check, it will default to returning `false`. You can change this behavior on a per-invocation basis by providing a fallback value as a second argument.\n\nFor instance, `unleash.IsEnabled(\"SuperAwesomeFeature\")` would return `false` if `SuperAwesomeFeature` doesn't exist. But if you'd rather it returned `true`, then you could pass that as the second argument:\n\n```csharp\nunleash.IsEnabled(\"SuperAwesomeFeature\", true)\n```\n\n#### Providing context\n\nYou can also **provide an [Unleash context](https://docs.getunleash.io/reference/unleash-context)** to the `IsEnabled` method:\n\n```csharp\nvar context = new UnleashContext\n{\n  UserId = \"61\"\n};\n\nunleash.IsEnabled(\"someToggle\", context);\n```\n\nRefer to the [Unleash context](#unleash-context) section for more information about using the Unleash context in the .NET SDK.\n\n## Handling events\n\nCurrently supported events:\n-  [Impression data events](https://docs.getunleash.io/advanced/impression-data#impression-event-data)\n-  Error events\n-  Toggles updated event\n\n```csharp\n\nvar settings = new UnleashSettings()\n{\n    // ...\n};\n\nvar unleash = new DefaultUnleash(settings);\n\n// Set up handling of impression and error events\nunleash.ConfigureEvents(cfg =\u003e\n{\n    cfg.ImpressionEvent = evt =\u003e { Console.WriteLine($\"{evt.FeatureName}: {evt.Enabled}\"); };\n    cfg.ErrorEvent = evt =\u003e { /* Handling code here */ Console.WriteLine($\"{evt.ErrorType} occured.\"); };\n    cfg.TogglesUpdatedEvent = evt =\u003e { /* Handling code here */ Console.WriteLine($\"Toggles updated on: {evt.UpdatedOn}\"); };\n});\n\n```\n\n\n## Activation strategies\n\nThe .Net client comes with implementations for the built-in activation strategies provided by unleash.\n\n- DefaultStrategy\n- UserWithIdStrategy\n- GradualRolloutRandomStrategy\n- GradualRolloutUserWithIdStrategy\n- GradualRolloutSessionIdStrategy\n- RemoteAddressStrategy\n- ApplicationHostnameStrategy\n- FlexibleRolloutStrategy\n\nRead more about the strategies in [the activation strategy reference docs](https://docs.getunleash.io/reference/activation-strategies).\n\n### Custom strategies\n\nYou can also specify and implement your own [custom strategies](https://docs.getunleash.io/reference/custom-activation-strategies). The specification must be registered in the Unleash UI and you must register the strategy implementation when you wire up unleash.\n\n```csharp\nIStrategy s1 = new MyAwesomeStrategy();\nIStrategy s2 = new MySuperAwesomeStrategy();\n\nIUnleash unleash = new DefaultUnleash(config, s1, s2);\n```\n\n## Unleash context\n\nIn order to use some of the common activation strategies you must provide an [Unleash context](https://docs.getunleash.io/reference/unleash-context).\n\nIf you have configured custom stickiness and want to use that with the FlexibleRolloutStrategy or Variants, add the custom stickiness parameters to the Properties dictionary on the Unleash Context:\n\n```csharp\nHttpContext.Current.Items[\"UnleashContext\"] = new UnleashContext\n{\n    UserId = HttpContext.Current.User?.Identity?.Name,\n    SessionId = HttpContext.Current.Session?.SessionID,\n    RemoteAddress = HttpContext.Current.Request.UserHostAddress,\n    Properties = new Dictionary\u003cstring, string\u003e\n    {\n        // Obtain \"customField\" and add it to the context properties\n        { \"customField\", HttpContext.Current.Items[\"customField\"].ToString() }\n    }\n};\n```\n\n### UnleashContextProvider\n\nThe provider typically binds the context to the same thread as the request. If you are using Asp.Net the `UnleashContextProvider` will typically be a 'request scoped' instance.\n\n\n```csharp\npublic class AspNetContextProvider : IUnleashContextProvider\n{\n    public UnleashContext Context\n    {\n       get { return HttpContext.Current?.Items[\"UnleashContext\"] as UnleashContext; }\n    }\n}\n\nprotected void Application_BeginRequest(object sender, EventArgs e)\n{\n    HttpContext.Current.Items[\"UnleashContext\"] = new UnleashContext\n    {\n        UserId = HttpContext.Current.User?.Identity?.Name,\n        SessionId = HttpContext.Current.Session?.SessionID,\n        RemoteAddress = HttpContext.Current.Request.UserHostAddress,\n        Properties = new Dictionary\u003cstring, string\u003e()\n        {\n            {\"UserRoles\", \"A,B,C\"}\n        }\n    };\n}\n\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\"),\n    UnleashContextProvider = new AspNetContextProvider(),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n      {\"Authorization\", \"API token\" }\n    }\n};\n```\n\n## Custom HTTP headers\n\nIf you want the client to send custom HTTP Headers with all requests to the Unleash api you can define that by setting them via the `UnleashSettings`.\n\n```csharp\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\"),\n    UnleashContextProvider = new AspNetContextProvider(),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n        {\"Authorization\", \"API token\" }\n    }\n};\n```\n\n### HttpMessageHandlers/Custom HttpClient initialization\nIf you need to specify HttpMessageHandlers or to control the instantiation of the HttpClient, you can create a custom\nHttpClientFactory that inherits from DefaultHttpClientFactory, and override the method CreateHttpClientInstance.\nThen configure UnleashSettings to use your custom HttpClientFactory.\n\n```csharp\ninternal class CustomHttpClientFactory : DefaultHttpClientFactory\n{\n    protected override HttpClient CreateHttpClientInstance(Uri unleashApiUri)\n    {\n        var messageHandler = new CustomHttpMessageHandler();\n        var httpClient = new HttpClient(messageHandler)\n        {\n            BaseAddress = apiUri,\n            Timeout = TimeSpan.FromSeconds(5)\n        };\n    }\n}\n\nvar settings = new UnleashSettings\n{\n    AppName = \"dotnet-test\",\n    //...\n    HttpClientFactory = new CustomHttpClientFactory()\n};\n```\n\n### Dynamic custom HTTP headers\nIf you need custom http headers that change during the lifetime of the client, a provider can be defined via the `UnleashSettings`.\n\n```vb\nPublic Class CustomHttpHeaderProvider\n    Implements IUnleashCustomHttpHeaderProvider\n\n    Public Function GetCustomHeaders() As Dictionary(Of String, String) Implements IUnleashCustomHttpHeaderProvider.GetCustomHeaders\n        Dim token = ' Acquire or refresh a token\n        Return New Dictionary(Of String, String) From\n                {{\"Authorization\", \"Bearer \" \u0026 token}}\n    End Function\nEnd Class\n\n' ...\n\nDim unleashSettings As New UnleashSettings()\nunleashSettings.AppName = \"dotnet-test\"\nunleashSettings.InstanceTag = \"instance z\"\n' add the custom http header provider to the settings\nunleashSettings.UnleashCustomHttpHeaderProvider = New CustomHttpHeaderProvider()\nunleashSettings.UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\")\nunleashSettings.UnleashContextProvider = New AspNetContextProvider()\nDim unleash = New DefaultUnleash(unleashSettings)\n\n```\n\n## Logging\n\nBy default Unleash-client uses LibLog to integrate with the currently configured logger for your application.\nThe supported loggers are:\n- Serilog\n- NLog\n- Log4Net\n- EntLib\n- Loupe\n\n### Custom logger integration\nTo plug in your own logger you can implement the `ILogProvider` interface, and register it with Unleash:\n\n```csharp\nUnleash.Logging.LogProvider.SetCurrentLogProvider(new CustomLogProvider());\nvar settings = new UnleashSettings()\n//...\n```\n\n The `GetLogger` method is responsible for returning a delegate to be used for logging, and your logging integration should be placed inside that delegate:\n\n```csharp\nusing System;\nusing Unleash.Logging;\n\nnamespace Unleash.Demo.CustomLogging\n{\n    public class CustomLogProvider : ILogProvider\n    {\n        public Logger GetLogger(string name)\n        {\n            return (logLevel, messageFunc, exception, formatParameters) =\u003e\n            {\n                // Plug in your logging code here\n\n                return true;\n            };\n        }\n\n        public IDisposable OpenMappedContext(string key, object value, bool destructure = false)\n        {\n            return new EmptyIDisposable();\n        }\n\n        public IDisposable OpenNestedContext(string message)\n        {\n            return new EmptyIDisposable();\n        }\n    }\n\n    public class EmptyIDisposable : IDisposable\n    {\n        public void Dispose()\n        {\n        }\n    }\n}\n```\n\n\n\n## Local backup\nBy default unleash-client fetches the feature toggles from unleash-server every 20s, and stores the result in temporary .json file which is located in `System.IO.Path.GetTempPath()` directory. This means that if the unleash-server becomes unavailable, the unleash-client will still be able to toggle the features based on the values stored in .json file. As a result of this, the second argument of `IsEnabled` will be returned in two cases:\n\n* When .json file does not exists\n* When the named feature toggle does not exist in .json file\n\nThe backup file name will follow this pattern: `{fileNameWithoutExtension}-{AppName}-{InstanceTag}-{SdkVersion}.{extension}`, where InstanceTag is either what you configure on `UnleashSettings` during startup, or a formatted string with a random component following this pattern: `{Dns.GetHostName()}-generated-{Guid.NewGuid()}`.\n\nYou can configure InstanceTag like this:\n\n```csharp\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\"),\n    // Set an instance tag for consistent backup file naming\n    InstanceTag = \"CustomInstanceTag\",\n    UnleashContextProvider = new AspNetContextProvider(),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n        {\"Authorization\", \"API token\" }\n    }\n};\n```\n\n## Bootstrapping\n* Unleash supports bootstrapping from a JSON string.\n* Configure your own custom provider implementing the `IToggleBootstrapProvider` interface's single method `ToggleCollection Read()`.\n  This should return a `String` that represents the API response from `{unleash_url}/api/client/features`\n* Example bootstrap files can be found in the json files located in [tests/Unleash.Tests/App_Data](tests/Unleash.Tests/App_Data)\n* Our assumption is this can be use for applications deployed to ephemeral containers or more locked down file systems where Unleash's need to write the backup file is not desirable or possible.\n* Loading with bootstrapping defaults to override feature toggles loaded from Local Backup, this override can be switched off by setting the `UnleashSettings.ToggleOverride` property to `false`\n\nConfiguring with the UnleashSettings:\n```csharp\nvar settings = new UnleashSettings()\n{\n    AppName = \"dotnet-test\",\n    UnleashApi = new Uri(\"http://unleash.herokuapp.com/api/\"),\n    CustomHttpHeaders = new Dictionary\u003cstring, string\u003e()\n    {\n      {\"Authorization\",\"API token\" }\n    },\n    ToggleOverride = false, // Defaults to true\n    ToggleBootstrapProvider = new MyToggleBootstrapProvider() // A toggle bootstrap provider implementing IToggleBootstrapProvider here\n};\n```\n\n### Provided Bootstrappers\n* Two ToggleBootstrapProviders are provided\n* These are found in the `Unleash.Utilities`:\n\n#### ToggleBootstrapFileProvider\n* Unleash comes with a `ToggleBootstrapFileProvider` which implements the `IToggleBootstrapProvider` interface.\n* Configure with `UnleashSettings` helper method:\n\n```csharp\nsettings.UseBootstrapFileProvider(\"./path/to/file.json\");\n```\n\n#### ToggleBootstrapUrlProvider\n* Unleash also comes with a `ToggleBootstrapUrlProvider` which implements the `IToggleBootstrapProvider` interface.\n* Fetches JSON from a webaddress using `HttpMethod.Get`\n\n* Configure with `UnleashSettings` helper method:\n\n```csharp\nvar shouldThrowOnError = true; // Throws for 500, 404, etc\nvar customHeaders = new Dictionary\u003cstring, string\u003e()\n{\n    { \"Authorization\", \"Bearer ABCdefg123\" } // Or whichever set of headers would be required to GET this file\n}; // Defaults to null\nsettings.UseBootstrapUrlProvider(\"://domain.top/path/to/file.json\", shouldThrowOnError, customHeaders);\n```\n\n## Run unleash server with Docker locally\nThe Unleash team have made a separate project which runs unleash server inside docker. Please see [unleash-docker](https://github.com/Unleash/unleash-docker) for more details.\n\n## Development\n\n### Setup/Tool suggestions/Requirements\nVisual Studio Community / VS Code / JetBrains Rider\nMicrosoft C# Dev Kit extension for VS Code\n.NET 6\n\n### Build/Test\nCode lives in `./src/Unleash`\nTests live in `./tests/Unleash.Tests`\n- Build: `dotnet build`\n- Test: `dotnet test` - This also executes spec tests\n\n### Formatting\n\nWe enforce formatting with `dotnet format`. This can be installed using the following command:\n\n`dotnet tool install -g dotnet-format`.\n\n### Release process\n- Draft a new release in releases, target `main`\n- Choose a new version (ie `4.1.9` without `v`)\n- Input new version number as tag, choose `create new tag \u003cx.x.x\u003e on publish`\n- Set the same version number as `Release title`\n- The button `Generate release notes` should give a summary of new commits and contributors\n- Choose to `set as the latest release`\n- Click `Publish release`.\nThis starts the release workflow which builds the new release and pushes the artifacts to NuGet\n\n## Other information\n\n- Check out our guide for more information on how to build and scale [feature flag](https://docs.getunleash.io/topics/feature-flags/feature-flag-best-practices) systems\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funleash%2Funleash-client-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funleash%2Funleash-client-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funleash%2Funleash-client-dotnet/lists"}