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; }
}

Select - Projection

Project specific fields using Select (server-side projection).
Fetches only requested Props fields, reducing data transfer.
var sw = Stopwatch.StartNew();

// Server-side projection - fetches only selected Props fields
var query = redb.Query<EmployeeProps>()
    .Take(100)
    .Select(x => new { x.Props.FirstName, x.Props.LastName, x.Props.Salary });

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

var results = await query.ToListAsync();

sw.Stop();

var first = results.FirstOrDefault();
var fullName = first != null ? $"{first.FirstName} {first.LastName}" : "N/A";
SelectProjectionFieldsIRedbQueryable.Select
MS 13ms(100)
MS Pro 51ms(100)
PG 53ms(100)
PG Pro 27ms(100)

Distinct - Unique by Props

Get unique objects by Props hash using Distinct().
Server-side: SELECT DISTINCT ON (_hash) - deduplicates by Props content.
var sw = Stopwatch.StartNew();

// Server-side Distinct by Props hash
var query = redb.Query<EmployeeProps>()
    .Distinct()
    .Take(100);

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

var uniqueEmployees = await query.ToListAsync();

// Compare with total count
var totalCount = await redb.Query<EmployeeProps>().CountAsync();

sw.Stop();
DistinctUniqueDedupeIRedbQueryable.Distinct
MS 1684ms(100)
MS Pro 70ms(100)
PG 619ms(100)
PG Pro 51ms(100)

OrderBy - Sort by Salary

Sort employees by salary using OrderBy.
Get top 5 lowest and highest earners.
var sw = Stopwatch.StartNew();

// Ascending - lowest earners
var lowest = await redb.Query<EmployeeProps>()
    .OrderBy(e => e.Salary)
    .Take(5)
    .ToListAsync();

// Descending - highest earners
var highest = await redb.Query<EmployeeProps>()
    .OrderByDescending(e => e.Salary)
    .Take(5)
    .ToListAsync();

sw.Stop();

var lowestSalary = lowest.FirstOrDefault()?.Props.Salary ?? 0;
var highestSalary = highest.FirstOrDefault()?.Props.Salary ?? 0;
OrderBySort
MS 232ms(10)
PG 200ms(10)

OrderBy + ThenBy

Sort employees by multiple fields using ThenBy.
Sort by department, then by salary descending.
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .OrderBy(e => e.Department)
    .ThenByDescending(e => e.Salary)
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();

var first = result.FirstOrDefault();
var info = first != null ? $"{first.Props.Department}: ${first.Props.Salary:N0}" : "-";
OrderByThenBySort
MS 388ms(100)
PG 289ms(100)
PG Pro 76ms(100)

Skip/Take - Pagination

Paginate employees using Skip and Take.
Get page 2 with 10 items per page.
var sw = Stopwatch.StartNew();

const int pageSize = 10;
const int page = 2; // 0-based would skip 10

var query = redb.Query<EmployeeProps>()
    .OrderBy(e => e.LastName)
    .Skip((page - 1) * pageSize)
    .Take(pageSize);

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

var result = await query.ToListAsync();
sw.Stop();
SkipTakePagination
MS 71ms(10)
MS Pro 64ms(10)
PG 562ms(10)
PG Pro 46ms(10)

OrderByDescending - Reverse Sort

Sort employees in descending order using OrderByDescending.
Get top earners and most experienced employees.
var sw = Stopwatch.StartNew();

// Top earners (highest salary first)
var query = redb.Query<EmployeeProps>()
    .OrderByDescending(e => e.Salary)
    .Take(5);

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

var topEarners = await query.ToListAsync();

// Oldest employees (highest age first)
var oldest = await redb.Query<EmployeeProps>()
    .OrderByDescending(e => e.Age)
    .Take(5)
    .ToListAsync();

sw.Stop();

var topSalary = topEarners.FirstOrDefault()?.Props.Salary ?? 0;
var oldestAge = oldest.FirstOrDefault()?.Props.Age ?? 0;
OrderByDescendingSortDescendingIRedbQueryable.OrderByDescending
MS 270ms(10)
MS Pro 114ms(10)
PG 202ms(10)

Tree Cleanup - Remove All

Cleanup existing tree data before E088/E089 tests.
Run this before E088 or E089 to ensure clean state.
Uses DeleteWithPurgeAsync for permanent removal.
await redb.SyncSchemeAsync<DepartmentProps>();

var sw = Stopwatch.StartNew();

var existing = await redb.TreeQuery<DepartmentProps>().ToListAsync();
var count = existing.Count;

if (count > 0)
{
    await redb.DeleteWithPurgeAsync(existing.Select(e => e.Id).ToList(), batchSize: 50);
}

sw.Stop();
TreeCleanupDeleteWithPurgeAsyncPro
MS 3ms(0)
MS Pro 9127ms(202)
PG 16771ms(202)
PG Pro 16884ms(202)

Tree Create BULK - Fast

BULK create tree hierarchy (Pro feature) - FAST version.
Creates ~100 tree nodes using pre-generated IDs + AddNewObjectsAsync.
Compare with E089 (sequential CreateChildAsync).
Structure:
  • 1 root (TechCorp)
  • 10 regional offices
  • 50 departments (5 per office)
  • 40 teams (4 per first 10 departments)
Optimization:
  • NextObjectIdBatchAsync - get all IDs in 1 query
  • AddNewObjectsAsync - bulk insert all nodes in 1 operation
await redb.SyncSchemeAsync<DepartmentProps>();
// Run E087 first to cleanup existing data

var totalNodes = 1 + OfficeCount + (OfficeCount * DeptPerOffice) + (TeamsForFirstDepts * TeamsPerDept);

// Measure bulk creation
var sw = Stopwatch.StartNew();

// 1. Get all IDs in ONE query
var ids = await redb.Context.NextObjectIdBatchAsync(totalNodes);

// 2. Create all objects with pre-assigned IDs and parent_id
var nodes = GenerateTree(ids);

// 3. Bulk insert ALL nodes in ONE operation
await redb.AddNewObjectsAsync(nodes.Cast<IRedbObject<DepartmentProps>>().ToList());

sw.Stop();

var rate = nodes.Count * 1000 / Math.Max(sw.ElapsedMilliseconds, 1);
GenerateTree
private static List<TreeRedbObject<DepartmentProps>> GenerateTree(long[] ids)
    {
        var nodes = new List<TreeRedbObject<DepartmentProps>>();
        var idx = 0;

        // Level 0: Root
        var rootId = ids[idx++];
        nodes.Add(CreateNode(rootId, null, "TechCorp", "CORP", "Headquarters", 50_000_000m));

        // Level 1: Regional offices
        var officeIds = new long[OfficeCount];
        for (int i = 0; i < OfficeCount; i++)
        {
            officeIds[i] = ids[idx++];
            var city = Cities[i % Cities.Length];
            nodes.Add(CreateNode(officeIds[i], rootId, $"{city} Office", $"OFF-{i + 1:D2}", $"Regional office {city}", 5_000_000m - i * 200_000m));
        }

        // Level 2: Departments in each office
        var deptIds = new List<long>();
        for (int o = 0; o < OfficeCount; o++)
        {
            for (int d = 0; d < DeptPerOffice; d++)
            {
                var deptId = ids[idx++];
                deptIds.Add(deptId);
                var deptName = Departments[d % Departments.Length];
                nodes.Add(CreateNode(deptId, officeIds[o], $"{deptName} {o + 1}-{d + 1}", $"DEPT-{o + 1:D2}-{d + 1:D2}", deptName, 1_000_000m - d * 100_000m));
            }
        }

        // Level 3: Teams in first 10 departments
        for (int d = 0; d < TeamsForFirstDepts && d < deptIds.Count; d++)
        {
            for (int t = 0; t < TeamsPerDept; t++)
            {
                var teamId = ids[idx++];
                var teamName = Teams[t % Teams.Length];
                nodes.Add(CreateNode(teamId, deptIds[d], $"{teamName} Team {d + 1}-{t + 1}", $"TEAM-{d + 1:D2}-{t + 1:D2}", teamName, 300_000m - t * 50_000m));
            }
        }

        return nodes;
    }
TreeBulkAddNewObjectsAsyncPro
MS 116ms(101)
MS Pro 244ms(101)
PG 220ms(101)
PG Pro 221ms(101)

Tree Create Sequential - Slow

Sequential create tree hierarchy (Pro feature) - SLOW version.
Creates ~100 tree nodes using sequential CreateChildAsync.
Compare with E088 (bulk AddNewObjectsAsync).
Structure:
  • 1 root (TechCorp)
  • 10 regional offices
  • 50 departments (5 per office)
  • 40 teams (4 per first 10 departments)
Each CreateChildAsync = separate DB round-trip.
await redb.SyncSchemeAsync<DepartmentProps>();
// Run E087 first to cleanup existing data

// Measure sequential creation
var sw = Stopwatch.StartNew();
var count = await CreateTreeSequentially(redb);
sw.Stop();

var rate = count * 1000 / Math.Max(sw.ElapsedMilliseconds, 1);
CreateTreeSequentially
private static async Task<int> CreateTreeSequentially(IRedbService redb)
    {
        var count = 0;

        // Level 0: Root
        var root = CreateDept("TechCorp", "CORP", "Headquarters", 50_000_000m);
        root.id = await redb.SaveAsync(root);
        count++;

        // Level 1: Regional offices
        var offices = new TreeRedbObject<DepartmentProps>[OfficeCount];
        for (int i = 0; i < OfficeCount; i++)
        {
            var city = Cities[i % Cities.Length];
            offices[i] = CreateDept($"{city} Office", $"OFF-{i + 1:D2}", $"Regional office {city}", 5_000_000m - i * 200_000m);
            offices[i].id = await redb.CreateChildAsync(offices[i], root);
            count++;
        }

        // Level 2: Departments in each office
        var depts = new List<TreeRedbObject<DepartmentProps>>();
        for (int o = 0; o < OfficeCount; o++)
        {
            for (int d = 0; d < DeptPerOffice; d++)
            {
                var deptName = Departments[d % Departments.Length];
                var dept = CreateDept($"{deptName} {o + 1}-{d + 1}", $"DEPT-{o + 1:D2}-{d + 1:D2}", deptName, 1_000_000m - d * 100_000m);
                dept.id = await redb.CreateChildAsync(dept, offices[o]);
                depts.Add(dept);
                count++;
            }
        }

        // Level 3: Teams in first 10 departments
        for (int d = 0; d < TeamsForFirstDepts && d < depts.Count; d++)
        {
            for (int t = 0; t < TeamsPerDept; t++)
            {
                var teamName = Teams[t % Teams.Length];
                var team = CreateDept($"{teamName} Team {d + 1}-{t + 1}", $"TEAM-{d + 1:D2}-{t + 1:D2}", teamName, 300_000m - t * 50_000m);
                await redb.CreateChildAsync(team, depts[d]);
                count++;
            }
        }

        return count;
    }
TreeCreateChildAsyncSequentialPro
MS 7038ms(101)
MS Pro 7786ms(101)
PG 7074ms(101)
PG Pro 10190ms(101)

Tree Load - Full Hierarchy

Load full tree with LoadTreeAsync.
Requires E089 to run first (creates tree data).
Loads entire hierarchy from root with specified depth.
// Find root
var roots = await redb.TreeQuery<DepartmentProps>()
    .Where(d => d.Code == "CORP")
    .Take(1)
    .ToListAsync();

if (roots.Count == 0)
    return Fail("E090", "Tree Load - Full Hierarchy", ExampleTier.Free, 0, "No tree. Run E089 first.");

var root = (TreeRedbObject<DepartmentProps>)roots[0];

// Measure LoadTreeAsync
var sw = Stopwatch.StartNew();
var tree = await redb.LoadTreeAsync<DepartmentProps>(root, maxDepth: 5);
sw.Stop();

var nodeCount = CountNodes(tree);
CountNodes
private static int CountNodes(ITreeRedbObject<DepartmentProps> node)
    {
        var count = 1;
        foreach (var child in node.Children.OfType<ITreeRedbObject<DepartmentProps>>())
            count += CountNodes(child);
        return count;
    }
TreeLoadTreeAsyncHierarchyProIRedbService.LoadTreeAsyncTreeRedbObject
MS 341ms(101)
MS Pro 565ms(101)
PG 209ms(101)
PG Pro 261ms(101)