# 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>
