# Persistent Flags Across Requests

Most feature flag definitions result into the flag being either on or off based on an outside condition such as AlwaysOn/AlwaysOff, TimeWindow and Targetting when at 100%.

Enabling the flag for only a percentage of requests can result in the user having different values between requests, so the state needs to be stored. This is done by implementing the **ISessionManager** interface.

### Persisting flags in the user's Session

```csharp
public class HttpContextFeatureSessionManager : ISessionManager
{
    private readonly IHttpContextAccessor _contextAccessor;
    private const string SessionKeyPrefix = "feature_flag_";

    public HttpContextFeatureSessionManager(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public Task<bool?> GetAsync(string featureName)
    {
        bool keyExistsInHttpSession = _contextAccessor.HttpContext!
                      .Session
                      .TryGetValue(key: $"{SessionKeyPrefix}{featureName}",
                                   value: out byte[]? bytes);

        if (keyExistsInHttpSession)
        {
            return Task.FromResult((bool?)BitConverter.ToBoolean(bytes));
        }

        return Task.FromResult<bool?>(null);
    }

    public Task SetAsync(string featureName, bool enabled)
    {
        _contextAccessor.HttpContext!
                        .Session
                        .Set(key: $"{SessionKeyPrefix}{featureName}",
                             value: BitConverter.GetBytes(enabled));

        return Task.CompletedTask;
    }
}
```

Then it can be registered in **`Startup.cs`**

```csharp
services.AddTransient<ISessionManager, HttpContextFeatureSessionManager>();
services.AddTogglyWeb(options =>
    {
        options.AppKey = Configuration["Toggly:AppKey"];
        options.Environment = Configuration["Toggly:Environment"];
    });
```

### Persisting flags in Redis

The example below uses a query variable **u** as a user diferentiator, and caches the values for 30 minutes.

```csharp
public class RedisSessionManager : ISessionManager
{
    private readonly IDatabase _cache;
    private readonly IHttpContextAccessor _contextAccessor;
    private readonly IMemoryCache _memoryCache;
    private readonly IFeatureDefinitionProvider _featureDefinitionProvider;

    public RedisSessionManager(IDatabase cache, IHttpContextAccessor contextAccessor, IMemoryCache memoryCache, IFeatureDefinitionProvider featureDefinitionProvider)
    {
        _cache = cache;
        _contextAccessor = contextAccessor;
        _memoryCache = memoryCache;
        _featureDefinitionProvider = featureDefinitionProvider;
    }

    public async Task<bool?> GetAsync(string featureName)
    {
        if (!_contextAccessor.HttpContext!.Request.Query.ContainsKey("u")) return null;

        var username = _contextAccessor.HttpContext!.Request.Query["u"];
        
        var val = await _cache.HashGetAsync($"tc:{username}", featureName);

        if (val.HasValue)
        {
            await _cache.KeyExpireAsync($"tc:{username}", new TimeSpan(0, 30, 0));
            return val.Equals(1);
        }

        return null;
    }

    public async Task SetAsync(string featureName, bool enabled)
    {
        if (!_contextAccessor.HttpContext!.Request.Query.ContainsKey("u")) return;

        var username = _contextAccessor.HttpContext!.Request.Query["u"];

        var definition = await _featureDefinitionProvider.GetFeatureDefinitionAsync(featureName);
        if (definition.EnabledFor.Any(t => t.Name.In("Microsoft.TimeWindow")))
            return;

        await _cache.HashSetAsync($"tc:{username}", featureName, enabled ? 1 : 0, When.Always);
        await _cache.KeyExpireAsync($"tc:{username}", new TimeSpan(0, 30, 0));
    }
}
```

Then it can be registered in **`Startup.cs`**

<pre class="language-csharp"><code class="lang-csharp">services.AddTransient&#x3C;ISessionManager, RedisSessionManager>();
services.AddTogglyWeb(options =>
<strong>    {
</strong>        options.AppKey = Configuration["Toggly:AppKey"];
        options.Environment = Configuration["Toggly:Environment"];
    });

services.AddSingleton(cfg =>
{
    IConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect(Configuration["ConnectionStrings:Redis"]);
    return multiplexer.GetDatabase();
});
</code></pre>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.toggly.io/feature-flags/feature-flags-in-c-.net/persistent-flags-across-requests.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
