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

Sql.Function - COALESCE

Demonstrates Sql.Function for calling custom SQL functions.
Uses COALESCE to safely handle potentially NULL values in comparisons.
Pro feature: arbitrary SQL function calls via Sql.Function<T>().
var sw = Stopwatch.StartNew();

// COALESCE returns first non-NULL argument
// Useful for NULL-safe comparisons: COALESCE(Age, 0) > 25
var query = redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<int>("COALESCE", e.Age, 0) > 30)
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<int>("COALESCE", e.Age, 0) > 30)
    .CountAsync();

sw.Stop();

var ages = results.Take(5).Select(e => e.Props.Age);
Sql.FunctionCOALESCENULLSql.Function
MS Pro 124ms(4165)
PG Pro 102ms(11562)

Sql.Function - POWER

Demonstrates Sql.Function for mathematical SQL functions.
Uses POWER to square the age and filter employees with Age^2 > 900 (Age > 30).
Pro feature: arbitrary SQL function calls via Sql.Function<T>().
var sw = Stopwatch.StartNew();

// POWER(Age, 2) > 900 means Age^2 > 900, so Age > 30
var query = redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<double>("POWER", e.Age, 2) > 900)
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<double>("POWER", e.Age, 2) > 900)
    .CountAsync();

sw.Stop();

var ages = results.Take(5).Select(e => $"{e.Props.Age} (^2={e.Props.Age * e.Props.Age})");
Sql.FunctionPOWERMathSql.Function
MS Pro 117ms(4165)
PG Pro 80ms(11562)

Sql.Function - Nested

Demonstrates nested Sql.Function calls for complex SQL expressions.
Uses SQRT(Salary) > 250 to find employees with Salary > 62,500.
Pro feature: nested arbitrary SQL function calls.
var sw = Stopwatch.StartNew();

// SQRT(Salary) > 250 means Salary > 62,500
var query = redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<double>("SQRT", e.Salary) > 250)
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => Sql.Function<double>("SQRT", e.Salary) > 250)
    .CountAsync();

sw.Stop();

var salaries = results.Take(5).Select(e => $"${e.Props.Salary:N0} (√={Math.Sqrt((double)e.Props.Salary):F0})");
Sql.FunctionSQRTNestedSql.Function
MS Pro 123ms(4703)
PG Pro 98ms(13053)

Base Fields - Single Object

Demonstrates using RedbObject base fields without Props (EAV values).
Base fields are stored directly in _objects table for fast access:
value_string, value_long, value_bool, value_double, value_datetime, value_guid, value_numeric, value_bytes.
Useful for simple objects, lookups, metadata without dynamic properties.
var sw = Stopwatch.StartNew();

// Create non-generic RedbObject with base fields only (no Props/EAV)
var obj = new RedbObject
{
    name = $"BaseFields_Example_{DateTime.UtcNow.Ticks}",
    value_string = "Example String Value",
    value_long = 12345,
    value_bool = true,
    value_double = 3.14159,
    value_datetime = DateTime.UtcNow,
    value_numeric = 99.99m
};

// Save - no _values table involved, only _objects
var id = await redb.SaveAsync(obj);

// Load back
var loaded = (await redb.LoadAsync(new[] { id })).FirstOrDefault();

sw.Stop();

if (loaded == null)
{
    return Fail("E164", "Base Fields - Single Object", ExampleTier.Free, sw.ElapsedMilliseconds,
        "Failed to load saved object.");
}
BaseFieldsRedbObjectNoPropsRedbObjectIRedbService.SaveAsync
MS 32ms(1)
MS Pro 40ms(1)
PG 67ms(1)
PG Pro 66ms(1)

Base Fields - Batch Operations

Demonstrates batch save/load of non-generic RedbObject with base fields.
Efficient for bulk operations on simple objects without Props.
All data stored in _objects table - no EAV overhead.
var sw = Stopwatch.StartNew();

// Create batch of non-generic objects
var objects = new List<RedbObject>();
var batchId = DateTime.UtcNow.Ticks;

for (int i = 0; i < 10; i++)
{
    objects.Add(new RedbObject
    {
        name = $"Batch_BaseFields_{batchId}_{i}",
        value_string = $"Item_{i}",
        value_long = i * 100,
        value_bool = i % 2 == 0
    });
}

// Batch save (cast to IRedbObject for batch API)
var ids = await redb.SaveAsync(objects.Cast<IRedbObject>());

// Batch load
var loaded = await redb.LoadAsync(ids);

sw.Stop();

if (loaded.Count != 10)
{
    return Fail("E165", "Base Fields - Batch Operations", ExampleTier.Free, sw.ElapsedMilliseconds,
        $"Expected 10 objects, got {loaded.Count}.");
}

// Verify data integrity
var verified = loaded.All(x => x.ValueString != null && x.ValueString.StartsWith("Item_"));
BaseFieldsBatchNonGenericRedbObjectIRedbService.SaveAsync
MS 37ms(10)
MS Pro 34ms(10)
PG 54ms(10)
PG Pro 60ms(10)

Generic with Props=null

Demonstrates using generic RedbObject<TProps> with Props=null.
Useful when you need scheme association but don't need EAV values.
Only base fields are stored - Props is explicitly null.
var sw = Stopwatch.StartNew();

// Ensure scheme exists
var scheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();

// Create generic object but with Props=null (only base fields)
var obj = new RedbObject<EmployeeProps>
{
    name = $"GenericNullProps_{DateTime.UtcNow.Ticks}",
    scheme_id = scheme.Id,
    value_string = "MetadataOnly",
    value_long = 999,
    value_bool = true
};
obj.Props = null!; // Explicitly null - no EAV values

// Save via interface (triggers non-Props save path)
var id = await redb.SaveAsync((IRedbObject)obj);

// Load as base IRedbObject (not generic)
var loaded = (await redb.LoadAsync(new[] { id })).FirstOrDefault();

sw.Stop();

if (loaded == null)
{
    return Fail("E166", "Generic with Props=null", ExampleTier.Free, sw.ElapsedMilliseconds,
        "Failed to load saved object.");
}
GenericNullPropsBaseFieldsRedbObject<TProps>IRedbService.SaveAsync
MS 38ms(1)
MS Pro 44ms(1)
PG 56ms(1)
PG Pro 59ms(1)

Mixed Batch - Generic and Non-Generic

Demonstrates mixed batch save with both non-generic RedbObject and generic RedbObject<T> with Props=null.
Shows flexibility of REDB to handle different object types in single batch operation.
var sw = Stopwatch.StartNew();

var batchId = DateTime.UtcNow.Ticks;
var objects = new List<IRedbObject>();

// Add non-generic objects
objects.Add(new RedbObject
{
    name = $"Mixed_NonGeneric_1_{batchId}",
    value_string = "NonGeneric",
    value_long = 1
});

objects.Add(new RedbObject
{
    name = $"Mixed_NonGeneric_2_{batchId}",
    value_string = "NonGeneric",
    value_long = 2
});

// Add generic objects with Props=null
var scheme = await redb.EnsureSchemeFromTypeAsync<EmployeeProps>();

var gen1 = new RedbObject<EmployeeProps>
{
    name = $"Mixed_Generic_1_{batchId}",
    scheme_id = scheme.Id,
    value_string = "Generic",
    value_long = 3
};
gen1.Props = null!;
objects.Add(gen1);

var gen2 = new RedbObject<EmployeeProps>
{
    name = $"Mixed_Generic_2_{batchId}",
    scheme_id = scheme.Id,
    value_string = "Generic",
    value_long = 4
};
gen2.Props = null!;
objects.Add(gen2);

// Batch save mixed types
var ids = await redb.SaveAsync(objects);

// Batch load
var loaded = await redb.LoadAsync(ids);

sw.Stop();

if (loaded.Count != 4)
{
    return Fail("E167", "Mixed Batch - Generic and Non-Generic", ExampleTier.Free, sw.ElapsedMilliseconds,
        $"Expected 4 objects, got {loaded.Count}.");
}

var nonGenCount = loaded.Count(x => x.ValueString == "NonGeneric");
var genCount = loaded.Count(x => x.ValueString == "Generic");
MixedBatchNonGenericGenericRedbObjectRedbObject<TProps>IRedbService.SaveAsync
MS 43ms(4)
MS Pro 40ms(4)
PG 61ms(4)
PG Pro 57ms(4)

Array - Contains

Demonstrates filtering by array element using Contains().
Server-side: = ANY(pvt."Skills") in PostgreSQL, STRING_SPLIT in MSSQL.
Pro feature: array operations in PVT.
var sw = Stopwatch.StartNew();

// Find employees who have "C#" skill
var query = redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && e.Skills.Contains("C#"))
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && e.Skills.Contains("C#"))
    .CountAsync();

sw.Stop();

var skillSamples = results.Take(3)
    .Select(e => $"{e.Props.FirstName}: [{string.Join(", ", e.Props.Skills?.Take(3) ?? [])}]");
ArrayContainsFilterIRedbQueryable.WhereArray.Contains
MS 1566ms(1133)
MS Pro 213ms(1028)
PG 696ms(2809)
PG Pro 124ms(2851)

Array - Contains OR

Demonstrates filtering by multiple array elements using Contains with OR.
Finds employees who have any of the specified skills.
Pro feature: multiple array operations in single query.
var sw = Stopwatch.StartNew();

// Find employees who have C# OR Python skill
var query = redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && 
        (e.Skills.Contains("C#") || e.Skills.Contains("Python")))
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && 
        (e.Skills.Contains("C#") || e.Skills.Contains("Python")))
    .CountAsync();

sw.Stop();

var csharpCount = results.Count(e => e.Props.Skills?.Contains("C#") == true);
var pythonCount = results.Count(e => e.Props.Skills?.Contains("Python") == true);
ArrayContainsORIRedbQueryable.WhereArray.Contains
MS 1415ms(1673)
MS Pro 261ms(1518)
PG 747ms(4149)
PG Pro 139ms(4211)

Array - Contains AND

Demonstrates filtering by multiple array elements using Contains with AND.
Finds employees who have ALL of the specified skills.
Pro feature: multiple array operations with AND logic.
var sw = Stopwatch.StartNew();

// Find employees who have BOTH C# AND SQL skills (full-stack)
var query = redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && 
        e.Skills.Contains("C#") && e.Skills.Contains("SQL"))
    .Take(100);

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

var results = await query.ToListAsync();
var totalCount = await redb.Query<EmployeeProps>()
    .Where(e => e.Skills != null && 
        e.Skills.Contains("C#") && e.Skills.Contains("SQL"))
    .CountAsync();

sw.Stop();

var skillSamples = results.Take(3)
    .Select(e => $"{e.Props.FirstName}: [{string.Join(", ", e.Props.Skills ?? [])}]");
ArrayContainsANDIRedbQueryable.WhereArray.Contains
MS 1334ms(1133)
MS Pro 258ms(1028)
PG 771ms(2809)
PG Pro 115ms(2851)