Code Examples

Learn RedBase by example. Browse working code samples for LINQ queries, bulk operations, tree structures, and more.

Data Model - EmployeeProps Click to view data model used in all examples
Tag with priority for categorization.
/// <summary>
/// Tag with priority for categorization.
/// </summary>
public class Tag
{
    public string Name { get; set; } = string.Empty;
    public int Priority { get; set; }
    public string? Description { get; set; }
}

/// <summary>
/// Project metrics for analytics.
/// </summary>
[RedbScheme("ProjectMetrics")]
public class ProjectMetricsProps
{
    public long ProjectId { get; set; }
    public long? TasksCompleted { get; set; }
    public long? TasksTotal { get; set; }
    public long? BugsFixed { get; set; }
    public double? Budget { get; set; }
    public long? TeamSize { get; set; }
    public Tag[]? Tags { get; set; }
    public string[]? Technologies { get; set; }
}

/// <summary>
/// Address with city, street and building details.
/// </summary>
public class Address
{
    public string City { get; set; } = string.Empty;
    public string Street { get; set; } = string.Empty;
    public BuildingInfo? Building { get; set; }
}

/// <summary>
/// Building information with floor and amenities.
/// </summary>
public class BuildingInfo
{
    public int Floor { get; set; }
    public string Name { get; set; } = string.Empty;
    public string[]? Amenities { get; set; }
    public int[]? AccessCodes { get; set; }
    public string[]? ParkingSpots { get; set; }
    public int[]? ElevatorFloors { get; set; }
}

/// <summary>
/// Contact detail key-value pair.
/// </summary>
public class ContactDetail
{
    public string Label { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
}

/// <summary>
/// Contact information (email, phone, etc).
/// </summary>
public class Contact
{
    public string Type { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
    public bool IsVerified { get; set; }
    public int[]? NotificationHours { get; set; }
    public ContactDetail[]? Metadata { get; set; }
}

/// <summary>
/// Department info with tags, metrics and nested budget data.
/// </summary>
public class Department
{
    public string Name { get; set; } = string.Empty;
    public int HeadCount { get; set; }
    public string[] Projects { get; set; } = [];
    public Tag[] Leaders { get; set; } = [];
    public Dictionary<string, int>? BudgetByYear { get; set; }
}

/// <summary>
/// Employee props - main model for examples.
/// Demonstrates all supported types:
/// - Simple types (int, string, DateTime, long, decimal)
/// - Arrays (string[], int[])
/// - Business classes (Address with nested BuildingInfo)
/// - Array of business classes (Contact[])
/// - RedbObject references (CurrentProject, PastProjects[])
/// - Dictionary types (various key and value types)
/// </summary>
[RedbScheme("Employee")]
public class EmployeeProps
{
    // Basic info
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public int Age { get; set; }
    public DateTime HireDate { get; set; }
    public string Position { get; set; } = string.Empty;
    public decimal Salary { get; set; }
    public string Department { get; set; } = string.Empty;
    public string? EmployeeCode { get; set; }

    // Skills and certifications (arrays)
    public string[]? Skills { get; set; }
    public int[]? SkillLevels { get; set; }
    public string[]? Certifications { get; set; }
    public int[]? CertificationYears { get; set; }

    // Addresses (business classes)
    public Address? HomeAddress { get; set; }
    public Address? WorkAddress { get; set; }
    public Address? EmergencyAddress { get; set; }

    // Contacts (array of business classes)
    public Contact[]? Contacts { get; set; }

    // Project references (RedbObject)
    public RedbObject<ProjectMetricsProps>? CurrentProject { get; set; }
    public RedbObject<ProjectMetricsProps>[]? PastProjects { get; set; }

    // Phone directory: extension -> phone number
    public Dictionary<string, string>? PhoneDirectory { get; set; }

    // Office locations by city
    public Dictionary<string, Address>? OfficeLocations { get; set; }

    // Bonus history by year
    public Dictionary<int, decimal>? BonusByYear { get; set; }

    // Department details with complex nested data (Pro)
    public Dictionary<string, Department>? DepartmentHistory { get; set; }

    // Composite key: (year, quarter) -> performance score (Pro)
    public Dictionary<(int Year, string Quarter), string>? PerformanceReviews { get; set; }

    // Project metrics by code (Pro)
    public Dictionary<string, RedbObject<ProjectMetricsProps>>? ProjectMetrics { get; set; }
}

/// <summary>
/// Department props for tree examples (corporate hierarchy).
/// Used with TreeRedbObject for organizational structure.
/// Similar to CategoryTestProps in ConsoleTest.
/// </summary>
[RedbScheme("Department")]
public class DepartmentProps
{
    /// <summary>Department name (e.g. "IT Department Moscow").</summary>
    public string Name { get; set; } = string.Empty;

    /// <summary>Description (e.g. "Software development team").</summary>
    public string Description { get; set; } = string.Empty;

    /// <summary>Is department active?</summary>
    public bool IsActive { get; set; } = true;

    /// <summary>Budget in USD.</summary>
    public decimal Budget { get; set; }

    /// <summary>Department code (e.g. "IT-MSK-DEV").</summary>
    public string Code { get; set; } = string.Empty;
}

/// <summary>
/// City props for list examples (linked objects).
/// Used as target object for RedbListItem.Object reference.
/// </summary>
[RedbScheme("City")]
public class CityProps
{
    /// <summary>City name.</summary>
    public string Name { get; set; } = string.Empty;
    
    /// <summary>Population count.</summary>
    public int Population { get; set; }
    
    /// <summary>Region or district.</summary>
    public string Region { get; set; } = string.Empty;
    
    /// <summary>Is capital city?</summary>
    public bool IsCapital { get; set; }
    
    /// <summary>GPS coordinates [lat, lon].</summary>
    public double[] Coordinates { get; set; } = [];
}

/// <summary>
/// Person props demonstrating ListItem fields.
/// Shows single ListItem and List of ListItems usage.
/// </summary>
[RedbScheme("Person")]
public class PersonProps
{
    /// <summary>Person name.</summary>
    public string Name { get; set; } = string.Empty;
    
    /// <summary>Age in years.</summary>
    public int Age { get; set; }
    
    /// <summary>Email address.</summary>
    public string Email { get; set; } = string.Empty;
    
    /// <summary>Single ListItem field (e.g. status from dictionary).</summary>
    public RedbListItem? Status { get; set; }
    
    /// <summary>Array of ListItems (e.g. roles from dictionary).</summary>
    public List<RedbListItem>? Roles { get; set; }
}

Delete - Single Object

Demonstrates deleting a single object by ID.
Creates a temporary object, then deletes it.
var sw = Stopwatch.StartNew();

// Create a temporary object to delete
var scheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();
var tempObj = new RedbObject<EmployeeProps>
{
    name = $"ToDelete_{DateTime.UtcNow.Ticks}",
    scheme_id = scheme.Id,
    Props = new EmployeeProps
    {
        FirstName = "Temporary",
        LastName = "Employee",
        Age = 25,
        Salary = 50000m,
        Department = "Test"
    }
};

var id = await redb.SaveAsync(tempObj);

// Verify it exists
var beforeDelete = await redb.LoadAsync<EmployeeProps>(id);
var existsBefore = beforeDelete != null;

// Delete the object
await redb.DeleteAsync(id);

// Verify it's deleted
var afterDelete = await redb.LoadAsync<EmployeeProps>(id);
var existsAfter = afterDelete != null;

sw.Stop();

if (!existsBefore || existsAfter)
{
    return Fail("E181", "Delete - Single Object", ExampleTier.Free, sw.ElapsedMilliseconds,
        $"Delete verification failed. Before: {existsBefore}, After: {existsAfter}");
}
DeleteSingleRemoveIRedbService.DeleteAsync
MS 143ms(1)
MS Pro 155ms(1)
PG 158ms(1)
PG Pro 198ms(1)

Delete - Batch

Demonstrates batch deletion of multiple objects.
Creates temporary objects, then deletes them all at once.
var sw = Stopwatch.StartNew();

// Create temporary objects to delete
var scheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();
var batchId = DateTime.UtcNow.Ticks;
var tempObjects = new List<RedbObject<EmployeeProps>>();

for (int i = 0; i < 5; i++)
{
    tempObjects.Add(new RedbObject<EmployeeProps>
    {
        name = $"BatchDelete_{batchId}_{i}",
        scheme_id = scheme.Id,
        Props = new EmployeeProps
        {
            FirstName = $"Temp{i}",
            LastName = "Employee",
            Age = 20 + i,
            Salary = 40000m + i * 1000,
            Department = "Test"
        }
    });
}

// Save all
var ids = await redb.SaveAsync(tempObjects.Cast<Core.Models.Contracts.IRedbObject>());

// Verify count before delete
var countBefore = (await redb.LoadAsync(ids)).Count;

// Delete all by IDs
foreach (var id in ids)
{
    await redb.DeleteAsync(id);
}

// Verify count after delete
var countAfter = (await redb.LoadAsync(ids)).Count;

sw.Stop();
DeleteBatchMultipleIRedbService.DeleteAsync
MS 306ms(5)
MS Pro 288ms(5)
PG 530ms(5)
PG Pro 537ms(5)

FirstOrDefaultAsync

Demonstrates FirstOrDefaultAsync to get a single object matching criteria.
Returns the first match or null if none found.
var sw = Stopwatch.StartNew();

// Get first employee with salary > 80000
var highEarner = await redb.Query<EmployeeProps>()
    .Where(e => e.Salary > 80000m)
    .OrderByDescending(e => e.Salary)
    .FirstOrDefaultAsync();

// Get first with impossible condition (should be null)
var notFound = await redb.Query<EmployeeProps>()
    .Where(e => e.Salary > 10_000_000m)
    .FirstOrDefaultAsync();

sw.Stop();

if (highEarner == null)
{
    return Fail("E183", "FirstOrDefaultAsync", ExampleTier.Free, sw.ElapsedMilliseconds,
        "No employees with salary > 80000 found.");
}
FirstSingleQueryIRedbQueryable.FirstOrDefaultAsync
MS 83ms(1)
MS Pro 79ms(1)
PG 91ms(1)
PG Pro 65ms(1)

Where - Nullable Field

Demonstrates filtering by nullable fields.
Finds objects where a nullable field is null or has value.
var sw = Stopwatch.StartNew();

// Find employees where EmployeeCode is null
var withoutCode = await redb.Query<EmployeeProps>()
    .Where(e => e.EmployeeCode == null)
    .Take(5)
    .ToListAsync();

var nullCount = await redb.Query<EmployeeProps>()
    .Where(e => e.EmployeeCode == null)
    .CountAsync();

// Find employees where EmployeeCode is NOT null
var withCode = await redb.Query<EmployeeProps>()
    .Where(e => e.EmployeeCode != null)
    .Take(5)
    .ToListAsync();

var notNullCount = await redb.Query<EmployeeProps>()
    .Where(e => e.EmployeeCode != null)
    .CountAsync();

sw.Stop();

var sampleCode = withCode.FirstOrDefault()?.Props.EmployeeCode ?? "N/A";
WhereNullableNullHasValueIRedbQueryable.Where
MS 113ms(5453)
MS Pro 137ms(4948)
PG 129ms(13772)
PG Pro 123ms(13731)

Where - Combined Conditions

Demonstrates filtering by conditions that can be simplified.
Shows different ways to filter including combined conditions.
var sw = Stopwatch.StartNew();

// Combined conditions: Age > 30 AND Salary > 70000
var expHighEarnersCount = await redb.Query<EmployeeProps>()
    .Where(e => e.Age > 30 && e.Salary > 70000m)
    .CountAsync();

var expHighEarners = await redb.Query<EmployeeProps>()
    .Where(e => e.Age > 30 && e.Salary > 70000m)
    .Take(5)
    .ToListAsync();

// OR conditions: Department == "IT" OR Department == "Engineering"
var techDeptCount = await redb.Query<EmployeeProps>()
    .Where(e => e.Department == "IT" || e.Department == "Engineering")
    .CountAsync();

sw.Stop();

var names = expHighEarners.Take(3).Select(e => $"{e.Props.FirstName} ({e.Props.Age}, ${e.Props.Salary:N0})");
WhereCombinedMultipleIRedbQueryable.Where
MS 122ms(4913)
MS Pro 167ms(4458)
PG 183ms(12193)
PG Pro 109ms(12375)

Object Reference - Load

Demonstrates loading objects with RedbObject references.
EmployeeProps has CurrentProject field of type RedbObject<ProjectMetricsProps>.
Shows how referenced objects are loaded.
var sw = Stopwatch.StartNew();

// Ensure schemes exist
var projectScheme = await redb.EnsureSchemeFromTypeAsync<ProjectMetricsProps>();
var employeeScheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();

// Create a project
var project = new RedbObject<ProjectMetricsProps>
{
    name = $"RefProject_{DateTime.UtcNow.Ticks}",
    scheme_id = projectScheme.Id,
    Props = new ProjectMetricsProps
    {
        ProjectId = 1001,
        TasksCompleted = 50,
        TasksTotal = 100,
        Budget = 500000
    }
};

var id1 = await redb.SaveAsync(project);
//Console.WriteLine($"First object id: {id1}");

// Create employee with project reference
var employee = new RedbObject<EmployeeProps>
{
    name = $"RefEmployee_{DateTime.UtcNow.Ticks}",
    scheme_id = employeeScheme.Id,
    Props = new EmployeeProps
    {
        FirstName = "John",
        LastName = "ProjectManager",
        Age = 35,
        Salary = 90000m,
        Department = "Engineering",
        CurrentProject = project // Reference to another RedbObject
    }
};

var id2 = await redb.SaveAsync(employee);
//Console.WriteLine($"Second object id: {id2}");

// Load employee and access referenced project
var loaded = await redb.LoadAsync<EmployeeProps>(employee.Id);

sw.Stop();

if (loaded?.Props?.CurrentProject == null)
{
    // Cleanup
    await redb.DeleteAsync(employee.Id);
    await redb.DeleteAsync(project.Id);
    
    return Fail("E186", "Object Reference - Load", ExampleTier.Free, sw.ElapsedMilliseconds,
        "CurrentProject reference not loaded.");
}

var projName = loaded.Props.CurrentProject.name;
var projBudget = loaded.Props.CurrentProject.Props?.Budget ?? 0;

// Cleanup
await redb.DeleteAsync(employee.Id);
await redb.DeleteAsync(project.Id);
ReferenceRelatedNestedRedbObject<TProps>
MS 157ms(1)
MS Pro 206ms(1)
PG 223ms(1)
PG Pro 265ms(1)

Update - Existing Object

Demonstrates updating an existing object by loading, modifying, and saving it.
Shows the standard update pattern in REDB: Load -> Modify -> Save.
var sw = Stopwatch.StartNew();

// Create an object to update
var scheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();
var original = new RedbObject<EmployeeProps>
{
    name = $"ToUpdate_{DateTime.UtcNow.Ticks}",
    scheme_id = scheme.Id,
    Props = new EmployeeProps
    {
        FirstName = "Original",
        LastName = "Name",
        Age = 30,
        Salary = 60000m,
        Department = "Sales"
    }
};
await redb.SaveAsync(original);
var originalSalary = original.Props.Salary;

// Load the object
var loaded = await redb.LoadAsync<EmployeeProps>(original.Id);
if (loaded == null)
{
    sw.Stop();
    return Fail("E187", "Update - Existing Object", ExampleTier.Free, sw.ElapsedMilliseconds,
        "Failed to load object for update.");
}

// Modify Props
loaded.Props.FirstName = "Updated";
loaded.Props.Salary = 75000m; // Raise!
loaded.Props.Department = "Engineering";

// Save changes (same ID = update)
await redb.SaveAsync(loaded);

// Verify changes
var verified = await redb.LoadAsync<EmployeeProps>(original.Id);

sw.Stop();

// Cleanup
await redb.DeleteAsync(original.Id);

if (verified == null)
{
    return Fail("E187", "Update - Existing Object", ExampleTier.Free, sw.ElapsedMilliseconds,
        "Failed to verify updated object.");
}
UpdateModifySaveIRedbService.LoadAsyncIRedbService.SaveAsync
MS 143ms(1)
MS Pro 106ms(1)
PG 133ms(1)
PG Pro 130ms(1)

SumAsync - With Filter

SumAsync with Where filter applied before aggregation.
Filter employees by department, then calculate total salary.
Tests Pro filter handling in Aggregation.
var sw = Stopwatch.StartNew();

// Uncomment to see generated SQL:
//var sql = await redb.Query<EmployeeProps>()
//    .Where(x => x.Department == "Engineering")
//    .ToSqlStringAsync();
//Console.WriteLine(sql);

// Sum salaries only for Engineering department
var engineeringSalary = await redb.Query<EmployeeProps>()
    .Where(x => x.Department == "Engineering")
    .SumAsync(x => x.Salary);

// Compare with total (no filter)
var totalSalary = await redb.Query<EmployeeProps>()
    .SumAsync(x => x.Salary);

sw.Stop();
SumWhereFilterAggregationIRedbQueryable.SumAsyncIRedbQueryable.Where
MS 38ms(1)
MS Pro 122ms(1)
PG 38ms(1)
PG Pro 71ms(1)

Window - With Filter

Window function with Where filter applied before windowing.
Filter employees by salary threshold, then apply ROW_NUMBER.
Tests Pro filter handling in Window queries.
var sw = Stopwatch.StartNew();

// Window with filter: only high earners (Salary > 70000)
var windowQuery = redb.Query<EmployeeProps>()
    .Where(x => x.Salary > 70000m)
    .Take(20)
    .WithWindow(w => w
        .PartitionBy(x => x.Department)
        .OrderByDesc(x => x.Salary));

// Uncomment to see generated SQL:
//var sql = await windowQuery.ToSqlStringAsync(x => new { x.Props.FirstName, Rank = Win.RowNumber() });
//Console.WriteLine(sql);

var ranked = await windowQuery.SelectAsync(x => new
{
    Name = x.Props.FirstName,
    Department = x.Props.Department,
    Salary = x.Props.Salary,
    Rank = Win.RowNumber()
});

sw.Stop();

var top = ranked.FirstOrDefault();
WindowWhereFilterRowNumberIRedbQueryable.WithWindowIRedbQueryable.WhereWin.RowNumber
MS 464ms(20)
MS Pro 341ms(20)
PG 132ms(20)
PG Pro 58ms(20)

AggregateAsync - With Filter

Multiple aggregations with Where filter applied before aggregation.
Filter employees by position, then calculate Sum, Avg, Min, Max.
Tests Pro filter handling in batch Aggregation.
var sw = Stopwatch.StartNew();

// Aggregate only Developers
var query = redb.Query<EmployeeProps>()
    .Where(x => x.Position == "Developer");

// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);

var stats = await query.AggregateAsync(x => new
{
    TotalSalary = Agg.Sum(x.Props.Salary),
    AvgSalary = Agg.Average(x.Props.Salary),
    MinSalary = Agg.Min(x.Props.Salary),
    MaxSalary = Agg.Max(x.Props.Salary),
    Count = Agg.Count()
});

sw.Stop();
AggregateWhereFilterSumAvgIRedbQueryable.AggregateAsyncIRedbQueryable.Where
MS 46ms(1)
MS Pro 87ms(1)
PG 54ms(1)
PG Pro 25ms(1)