.NETv1.2.1Backend

.NET SDK

Server-side feature flags for .NET 8+ with built-in circuit breaker, caching, SSE streaming, and zero external dependencies.

🛡️

Circuit Breaker

Auto-recovery

📦

Zero Dependencies

Only BCL types

💾

Caching

Persistent cache

SSE Support

Real-time updates

Installation

.NET CLI
dotnet add package Rollgate.SDK
PackageReference (.csproj)
<PackageReference Include="Rollgate.SDK" Version="1.2.1" />

Quick Start

Program.cs
using Rollgate.SDK;

var client = new RollgateClient(new RollgateConfig
{
    ApiKey = Environment.GetEnvironmentVariable("ROLLGATE_API_KEY"),
    BaseUrl = "https://api.rollgate.io",
    RefreshInterval = TimeSpan.FromSeconds(30),
});

await client.InitAsync();

// Check flags
bool enabled = client.IsEnabled("my-feature", false);

// Identify user
await client.IdentifyAsync(new UserContext
{
    Id = "user-123",
    Email = "[email protected]",
    Attributes = new Dictionary<string, object>
    {
        ["plan"] = "pro",
    },
});

// Cleanup
await client.CloseAsync();

Configuration

OptionTypeDefaultDescription
ApiKeystringrequiredServer API key (rg_server_...)
BaseUrlstring"https://api.rollgate.io"API base URL
RefreshIntervalTimeSpan30sPolling interval, TimeSpan.Zero to disable
EnableStreamingboolfalseUse SSE for real-time updates (opt-in)
TimeoutTimeSpan5sHTTP request timeout

Methods

InitAsync()

Initialize the client and fetch flags from the server. Must be called before evaluating any flags.

await client.InitAsync();

CloseAsync()

Gracefully shut down the client, stopping polling and SSE connections.

await client.CloseAsync();

IsEnabled(key, defaultValue)

Check if a boolean flag is enabled. Returns the flag value or the default.

bool showFeature = client.IsEnabled("new-feature", false);

if (showFeature)
{
    // New feature code
}

GetValue<T>(key, defaultValue)

Get a flag value of any type. Use for string, number, or JSON flags.

// String flag
string theme = client.GetValue<string>("theme", "light");

// Number flag
int maxRetries = client.GetValue<int>("max-retries", 3);

// JSON flag
var config = client.GetValue<RateLimitConfig>("rate-limit-config", new RateLimitConfig
{
    Enabled = false,
    Limit = 100,
});

GetString(key, defaultValue)

Type-safe helper for string flags.

string buttonColor = client.GetString("cta-color", "blue");
string variant = client.GetString("checkout-variant", "control");

GetNumber(key, defaultValue)

Type-safe helper for number flags.

double pageSize = client.GetNumber("results-per-page", 20);
double timeout = client.GetNumber("api-timeout-ms", 5000);

GetJson<T>(key, defaultValue)

Type-safe helper for JSON flags with generic type support. Uses System.Text.Json for deserialization.

public class FeatureConfig
{
    public bool Enabled { get; set; }
    public int MaxItems { get; set; }
    public List<string> AllowedRoles { get; set; } = new();
}

var config = client.GetJson<FeatureConfig>("feature-config", new FeatureConfig
{
    Enabled = false,
    MaxItems = 10,
    AllowedRoles = new List<string> { "admin" },
});

// Access typed properties
if (config.Enabled && config.AllowedRoles.Contains(user.Role))
{
    // Feature logic
}

GetAllFlags()

Get all flags as a dictionary.

Dictionary<string, object> flags = client.GetAllFlags();
// { "feature-a": true, "feature-b": false }

IdentifyAsync(user)

Set user context for targeting rules. Re-fetches flags with the new context.

await client.IdentifyAsync(new UserContext
{
    Id = "user-123",
    Email = "[email protected]",
    Attributes = new Dictionary<string, object>
    {
        ["plan"] = "enterprise",       // For "plan in pro,enterprise" rules
        ["country"] = "US",            // For "country equals US" rules
        ["company"] = "Acme Inc",      // For "company contains Acme" rules
        ["app_version"] = "2.1.0",     // For "app_version semver_gt 2.0.0" rules
    },
});

RefreshAsync()

Force refresh flags from the server.

await client.RefreshAsync();

Circuit Breaker

The SDK includes a circuit breaker that protects your app when Rollgate is unavailable. When open, it uses cached flag values.

// Get current state
string state = client.GetCircuitState();
// "closed" | "open" | "half-open"

// Listen to events
client.OnCircuitOpen += () =>
{
    Console.WriteLine("Circuit breaker opened - using cached flags");
};

client.OnCircuitClosed += () =>
{
    Console.WriteLine("Circuit breaker closed - normal operation");
};

// Force reset (use with caution)
client.ResetCircuit();

ASP.NET Core Integration

Register the Rollgate client as a singleton in the dependency injection container for use across your application.

Program.cs
using Rollgate.SDK;

var builder = WebApplication.CreateBuilder(args);

// Register RollgateClient as a singleton
builder.Services.AddSingleton(sp =>
{
    var client = new RollgateClient(new RollgateConfig
    {
        ApiKey = builder.Configuration["Rollgate:ApiKey"]!,
    });
    client.InitAsync().GetAwaiter().GetResult();
    return client;
});

var app = builder.Build();

app.MapControllers();
app.Run();
Controllers/FeatureController.cs
using Microsoft.AspNetCore.Mvc;
using Rollgate.SDK;

[ApiController]
[Route("api/[controller]")]
public class FeatureController : ControllerBase
{
    private readonly RollgateClient _rollgate;

    public FeatureController(RollgateClient rollgate) => _rollgate = rollgate;

    [HttpGet("feature")]
    public IActionResult GetFeature()
    {
        var enabled = _rollgate.IsEnabled("new-feature", false);
        return Ok(new { enabled });
    }

    [HttpGet("health")]
    public IActionResult Health()
    {
        var circuit = _rollgate.GetCircuitState();
        var isHealthy = circuit == "closed";

        return isHealthy ? Ok(new { status = "healthy", circuit })
                         : StatusCode(503, new { status = "degraded", circuit });
    }
}
Middleware example
// Middleware to identify user on each request
app.Use(async (context, next) =>
{
    var rollgate = context.RequestServices.GetRequiredService<RollgateClient>();
    var user = context.User;

    if (user.Identity?.IsAuthenticated == true)
    {
        await rollgate.IdentifyAsync(new UserContext
        {
            Id = user.FindFirst("sub")?.Value ?? "",
            Email = user.FindFirst("email")?.Value ?? "",
            Attributes = new Dictionary<string, object>
            {
                ["plan"] = user.FindFirst("plan")?.Value ?? "free",
            },
        });
    }

    await next();
});

Error Handling & Graceful Degradation

The SDK is designed to never throw exceptions during flag evaluation. If Rollgate is unavailable, flags return their default values. Use these patterns for production-grade reliability:

Graceful degradation
// Default values ensure your app works even without Rollgate
bool showNewFeature = client.IsEnabled("new-feature", false);
// Returns false if: flag doesn't exist, API down, circuit open

// Monitor degraded state in health checks
app.MapGet("/health", (RollgateClient rollgate) =>
{
    var circuit = rollgate.GetCircuitState();
    var isHealthy = circuit == "closed";

    return Results.Json(new
    {
        status = isHealthy ? "healthy" : "degraded",
        circuit,
        flagsSource = circuit == "open" ? "cache" : "live",
    }, statusCode: isHealthy ? 200 : 503);
});

Initialization Error Handling

try
{
    await client.InitAsync();
    Console.WriteLine($"Rollgate initialized with {client.GetAllFlags().Count} flags");
}
catch (Exception ex)
{
    // Log but don't crash - SDK will use defaults
    Console.Error.WriteLine($"Rollgate init failed: {ex.Message}");
    Console.WriteLine("Continuing with default flag values");
}

// App starts regardless of Rollgate status
app.Run();