Примеры кода

Изучайте RedBase на примерах. Просматривайте рабочие примеры кода для LINQ-запросов, массовых операций, древовидных структур и многого другого.

Data Model - EmployeeProps Нажмите, чтобы увидеть модель данных, используемую во всех примерах
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; }
}

/// <summary>
/// Product with a unique business key (V4 [RedbUnique]): the SKU is unique per scheme,
/// enforced by the database over the hash of the canonical value. Used by E003.
/// </summary>
public class ProductProps
{
    /// <summary>Unique business key. NULL never participates in uniqueness.</summary>
    [RedbUnique]
    public string? Sku { get; set; }

    /// <summary>Display name (not unique).</summary>
    public string Title { get; set; } = string.Empty;

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

Bulk Insert - Complex Objects

Example: Bulk insert of complex employee records.
Classic ORM (EF Core) would require ~28 tables:
  • Employees - main table
  • EmployeeSkills - string[] Skills (FK)
  • EmployeeSkillLevels - int[] SkillLevels (FK)
  • EmployeeCertifications - string[] (FK)
  • EmployeeCertificationYears - int[] (FK)
  • Addresses - for Home/Work/Emergency (FK)
  • BuildingInfos - nested in Address (FK)
  • BuildingAmenities - string[] in BuildingInfo (FK)
  • BuildingAccessCodes - int[] in BuildingInfo (FK)
  • BuildingParkingSpots - string[] (FK)
  • BuildingElevatorFloors - int[] (FK)
  • Contacts - Contact[] (FK)
  • ContactNotificationHours - int[] in Contact (FK)
  • ContactMetadata - ContactDetail[] in Contact (FK)
  • ProjectMetrics - RedbObject reference
  • ProjectTags - Tag[] in ProjectMetrics (FK)
  • ProjectTechnologies - string[] in ProjectMetrics (FK)
  • EmployeePastProjects - many-to-many (2 FK)
  • PhoneDirectory - Dictionary (FK)
  • OfficeLocations - Dictionary + Address (2 FK)
  • BonusByYear - Dictionary (FK)
  • DepartmentHistory - Dictionary (FK)
  • Departments - nested class
  • DepartmentProjects - string[] (FK)
  • DepartmentLeaders - Tag[] (FK)
  • DepartmentBudgetByYear - nested Dictionary (FK)
  • PerformanceReviews - tuple key Dictionary (FK)
  • ProjectMetricsDict - Dictionary with RedbObject (2 FK)
INSERTs per employee: ~40-60 records across 28 tables with FK ordering.
For 100 employees: 4000-6000 INSERTs!
REDB: 2 tables (_objects + _values), ~3000 records, single BulkInsert via COPY.
var sw = Stopwatch.StartNew();
const int count = 100;

var employees = CreateEmployees(count);

// Bulk insert using COPY protocol (Pro feature)
var savedIds = await redb.SaveAsync(employees); 

sw.Stop();

// Classic ORM: ~25 tables, ~5000 INSERTs with FK ordering
// REDB: 2 tables, ~3000 values, single BulkInsert
GetSkillsForPosition
private static string[] GetSkillsForPosition(string position, int index)
    {
        var skills = position switch
        {
            "Developer" => new[] { "C#", "SQL", "Azure", "Docker" },
            "Designer" => new[] { "Figma", "Photoshop", "CSS" },
            "Analyst" => new[] { "SQL", "Excel", "Python" },
            "Manager" => new[] { "Leadership", "Agile", "Communication" },
            _ => new[] { "Communication", "Teamwork" }
        };
        var take = position == "Developer" ? 2 + index % 3 : 2 + index % 2;
        return skills.Take(Math.Min(take, skills.Length)).ToArray();
    }
BulkInsertAddNewObjectsAsyncCOPY
MS Pro 1202ms(100)
postgres.ef 1708ms(100)
PG Pro 1069ms(100)
sqlite.pro 1891ms(100)

SaveAsync - Create Employee

Example: Create and save a new employee.
var sw = Stopwatch.StartNew();

var employee = new RedbObject<EmployeeProps>
{
    name = "New Developer",
    Props = new EmployeeProps
    {
        FirstName = "Alice",
        LastName = "Johnson",
        Age = 28,
        Position = "Developer",
        Department = "Engineering",
        Salary = 85000m,
        HireDate = DateTime.Today,
        EmployeeCode = "EMP-NEW-001",
        Skills = ["C#", "React", "SQL"]
    }
};

var id = await redb.SaveAsync(employee);
sw.Stop();
SaveAsyncCreateCRUD
MS Pro 77ms
mssql.pro.cache 66ms
mssql.pro.repeat 88ms
postgres.ef 85ms
postgres.ef.repeat 7ms
PG Pro 102ms
postgres.pro.cache 95ms
postgres.pro.repeat 114ms
sqlite.pro 8ms
sqlite.pro.cache 5ms
sqlite.pro.repeat 7ms

LoadAsync - Load Employee by ID

Example: Load employee by ID.
var sw = Stopwatch.StartNew();

// Get any existing employee ID
var query = redb.Query<EmployeeProps>().Take(1);
var items = await query.ToListAsync();

if (items.Count == 0)
{
    sw.Stop();
    return Fail("E002", "LoadAsync - Load Employee by ID", ExampleTier.Free, sw.ElapsedMilliseconds,
        "No employees found. Run E000 first.");
}

var id = items[0].Id;
var loaded = await redb.LoadAsync<EmployeeProps>(id);

if (loaded != null)
{
    EmployeeProps? employee = (EmployeeProps)loaded;
}

sw.Stop();
LoadAsyncReadCRUD
MS Pro 153ms
mssql.pro.cache 12ms
mssql.pro.repeat 46ms
postgres.ef 2318ms
postgres.ef.repeat 1018ms
PG Pro 126ms
postgres.pro.cache 4ms
postgres.pro.repeat 33ms
sqlite.pro 104ms
sqlite.pro.cache 0ms
sqlite.pro.repeat 12ms

Unique Keys - [RedbUnique] and value_unique

Unique business keys (V4 [RedbUnique]): the value of a marked field is unique within
its scheme, enforced by the DATABASE over the hash of the canonical value - not by a
pre-check that races. The contract shown here:
1. A duplicate save fails with a typed .
2. GetByUniqueAsync finds an object by its key value (server-side, hash lookup).
3. NULL never participates: any number of objects may leave the key empty.
4. Soft delete releases the key - the trash does not squat on business identifiers.
redb carries TWO unique-key mechanisms, both shown here:
  • [RedbUnique] on a Props field - per-FIELD uniqueness, any scalar type, hash in
_values._unique;
  • RedbObject.value_unique - a per-OBJECT string key on the object row itself
(scheme-wide, up to 440 chars, no Props field needed) - the natural slot for an external
identifier of the whole object.
await redb.SyncSchemeAsync<ProductProps>();

// Unique per run so the example is rerunnable on a shared database.
var sku = $"SKU-{Guid.NewGuid():N}"[..20];
var sw = Stopwatch.StartNew();

// 1. The first holder of the key wins.
var first = new RedbObject<ProductProps>
{
    name = "unique-demo-first",
    Props = new ProductProps { Sku = sku, Title = "First holder", Price = 9.99m }
};
var firstId = await redb.SaveAsync(first);

// 2. A duplicate is rejected by the database with a typed exception.
string rejected;
try
{
    await redb.SaveAsync(new RedbObject<ProductProps>
    {
        name = "unique-demo-duplicate",
        Props = new ProductProps { Sku = sku, Title = "Impostor", Price = 0.99m }
    });
    rejected = "NOT rejected - this line must never print";
}
catch (RedbUniqueViolationException ex)
{
    rejected = $"rejected with {nameof(RedbUniqueViolationException)} (property: {ex.PropertyName ?? "Sku"})";
}

// 3. Lookup by key value - one server-side hash probe, no scan.
var found = await redb.GetByUniqueAsync<ProductProps>(p => p.Sku, sku);

// 4. NULL keys do not collide: both keyless products save fine.
await redb.SaveAsync(new RedbObject<ProductProps> { name = "no-key-a", Props = new ProductProps { Title = "Draft A" } });
await redb.SaveAsync(new RedbObject<ProductProps> { name = "no-key-b", Props = new ProductProps { Title = "Draft B" } });

// 5. Soft delete releases the key for the next claimant.
await redb.DeleteAsync(firstId);
var successor = new RedbObject<ProductProps>
{
    name = "unique-demo-successor",
    Props = new ProductProps { Sku = sku, Title = "Successor", Price = 19.99m }
};
var successorId = await redb.SaveAsync(successor);

// 6. The OBJECT-level key: value_unique lives on the object row itself - no Props
//    field involved - and is unique across the whole scheme.
var objectKey = $"EXT-{Guid.NewGuid():N}"[..20];
var holder = new RedbObject<ProductProps>
{
    name = "object-key-holder",
    value_unique = objectKey,
    Props = new ProductProps { Title = "Holder of the object key" }
};
await redb.SaveAsync(holder);

string objectRejected;
try
{
    await redb.SaveAsync(new RedbObject<ProductProps>
    {
        name = "object-key-impostor",
        value_unique = objectKey,
        Props = new ProductProps { Title = "Impostor" }
    });
    objectRejected = "NOT rejected - this line must never print";
}
catch (RedbUniqueViolationException ex)
{
    // ex.Kind tells WHICH mechanism fired: ObjectKey here vs Property for [RedbUnique].
    objectRejected = $"rejected with {nameof(RedbUniqueViolationException)} (Kind: {ex.Kind})";
}

sw.Stop();
UniqueRedbUniqueValueUniqueGetByUniqueAsyncSoftDeleteIRedbService.GetByUniqueAsyncIRedbService.SaveAsync
PG Pro 692ms(5)
postgres.pro.repeat 651ms(5)
postgres.pro.cache 643ms(5)
MS Pro 506ms(5)
mssql.pro.repeat 605ms(5)
mssql.pro.cache 420ms(5)
sqlite.pro 37ms(5)
sqlite.pro.repeat 18ms(5)
sqlite.pro.cache 13ms(5)

Unique Keys - nested, subtree and element keys, [RedbTags]

Unique keys beyond a root scalar (V4). Props are stored flat, one row per value, and the
storage already maintains a canonical hash for every nested class and collection - so
[RedbUnique] reaches four more shapes with the same database-enforced index:
1. A scalar INSIDE a nested class - a lambda path (d => d.Identity!.Serial) is the key.
2. A whole SUBTREE - the content of a nested class, in canonical form, is the key.
3. A whole COLLECTION - the ordered list is the key; the same numbers in another order differ.
4. ELEMENTS of a collection with a scope: UniqueScope.Collection forbids duplicates
inside one object's collection, UniqueScope.Scheme makes every element value unique
across all objects of the scheme (and probeable by GetByUniqueAsync).
Plus [RedbTags]: a free-form marker written to the scheme and structure metadata for
applications and extensions to read back.
await redb.SyncSchemeAsync<DeviceProps>();

// Unique per run so the example is rerunnable on a shared database.
var run = Guid.NewGuid().ToString("N")[..8];
var sw = Stopwatch.StartNew();

// 1. Key on a scalar inside a nested class: same rules as a root key.
var serial = $"SN-{run}";
var alphaId = await redb.SaveAsync(Device("alpha", d => d.Identity = new DeviceIdentity { Serial = serial, Vendor = "Acme" }));
var nestedDup = await Rejected(() => redb.SaveAsync(Device("alpha-dup", d => d.Identity = new DeviceIdentity { Serial = serial, Vendor = "Other" })));
var bySerial = await redb.GetByUniqueAsync<DeviceProps>(d => d.Identity!.Serial, serial);

// 2. Subtree key: the CONTENT of the nested class is the key. Same content - rejected;
//    one field different - a different key. The lookup canonicalises the probe object
//    the same way the save did.
var region = $"eu-{run}";
var betaId = await redb.SaveAsync(Device("beta", d => d.Config = new DeviceConfig { Region = region, Tier = 2 }));
var subtreeDup = await Rejected(() => redb.SaveAsync(Device("beta-dup", d => d.Config = new DeviceConfig { Region = region, Tier = 2 })));
await redb.SaveAsync(Device("beta-tier3", d => d.Config = new DeviceConfig { Region = region, Tier = 3 }));
var byConfig = await redb.GetByUniqueAsync<DeviceProps>(d => d.Config, new DeviceConfig { Region = region, Tier = 2 });

// 3. Subtree key on a collection: the ordered list is the key.
var basePort = Random.Shared.Next(10_000, 60_000);
List<long> ports = [basePort, basePort + 1, basePort + 2];
await redb.SaveAsync(Device("gamma", d => d.Ports = ports));
var listDup = await Rejected(() => redb.SaveAsync(Device("gamma-dup", d => d.Ports = [.. ports])));
await redb.SaveAsync(Device("gamma-reversed", d => d.Ports = [basePort + 2, basePort + 1, basePort]));

// 4. Element keys, Collection scope: no duplicate label inside ONE device. Another device
//    may carry the same label - the scope is the collection, not the scheme.
await redb.SaveAsync(Device("delta", d => d.Labels = ["rack-1", "spare"]));
await redb.SaveAsync(Device("delta-twin", d => d.Labels = ["rack-1"]));
var elementDup = await Rejected(() => redb.SaveAsync(Device("delta-dup", d => d.Labels = ["rack-1", "rack-1"])));

// 5. Element keys, Scheme scope: a MAC address belongs to one device in the whole scheme,
//    and the holder is found by the element value.
var mac = $"02:{run[..2]}:{run[2..4]}:{run[4..6]}:00:01";
// The second address comes from the same run characters as the first: the key is unique across the
// scheme, and two characters alone (256 values) collide with earlier runs of this example.
var epsilonId = await redb.SaveAsync(Device("epsilon", d => d.MacAddresses = [mac, $"02:{run[..2]}:{run[2..4]}:{run[4..6]}:00:02"]));
var macDup = await Rejected(() => redb.SaveAsync(Device("epsilon-dup", d => d.MacAddresses = [mac])));
var byMac = await redb.GetByUniqueAsync<DeviceProps>(d => d.MacAddresses, mac);

// 6. [RedbTags]: the markers are metadata - read them back from the synced scheme.
var scheme = await redb.GetSchemeByTypeAsync<DeviceProps>();
var schemeTags = scheme?.Tags;
var macTags = scheme?.GetStructureByName(nameof(DeviceProps.MacAddresses))?.Tags;

sw.Stop();

// The example is a contract probe: a duplicate that slipped through, a lookup that missed
// or a tag that did not land is a FAIL, not a line in the report.
var problems = new List<string>();
if (!nestedDup.StartsWith("rejected")) problems.Add("nested key: duplicate was not rejected");
if (bySerial?.Id != alphaId) problems.Add("nested key: lookup by lambda path missed");
if (!subtreeDup.StartsWith("rejected")) problems.Add("subtree key: duplicate content was not rejected");
if (byConfig?.Id != betaId) problems.Add("subtree key: lookup by content missed");
if (!listDup.StartsWith("rejected")) problems.Add("collection key: duplicate list was not rejected");
if (!elementDup.StartsWith("rejected")) problems.Add("element key (Collection): duplicate inside one device was not rejected");
if (!macDup.StartsWith("rejected")) problems.Add("element key (Scheme): duplicate across devices was not rejected");
if (byMac?.Id != epsilonId) problems.Add("element key (Scheme): lookup by element value missed");
if (schemeTags != "examples,e004") problems.Add($"[RedbTags] on the scheme did not land: '{schemeTags}'");
if (macTags != "network") problems.Add($"[RedbTags] on the structure did not land: '{macTags}'");
if (problems.Count > 0)
    return Fail("E004", "Unique Keys - nested, subtree and element keys, [RedbTags]", ExampleTier.Free, sw.ElapsedMilliseconds, string.Join("; ", problems));
Device
private static RedbObject<DeviceProps> Device(string title, Action<DeviceProps> configure)
    {
        var props = new DeviceProps { Title = title };
        configure(props);
        return new RedbObject<DeviceProps> { name = $"e004-{title}", Props = props };
    }
Rejected
private static async Task<string> Rejected(Func<Task<long>> save)
    {
        try
        {
            await save();
            return "NOT rejected - this line must never print";
        }
        catch (RedbUniqueViolationException ex)
        {
            return $"rejected ({ex.Kind}, {ex.PropertyName})";
        }
    }
UniqueRedbUniqueUniqueScopeRedbTagsGetByUniqueAsyncIRedbService.GetByUniqueAsyncIRedbService.SaveAsyncIRedbService.GetSchemeByTypeAsync
PG Pro 1354ms(9)
postgres.pro.repeat 1292ms(9)
postgres.pro.cache 1275ms(9)
MS Pro 970ms(9)
mssql.pro.repeat 1258ms(9)
mssql.pro.cache 873ms(9)
sqlite.pro 57ms(9)
sqlite.pro.repeat 74ms(9)
sqlite.pro.cache 36ms(9)

Where - Filter by Salary

Example: Simple Where filter by salary.
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .Where(e => e.Salary > 75000m)
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();
WhereQueryFilterIRedbQueryable.WhereIRedbService.Query
PG Pro 155ms(100)
postgres.ef 1213ms(100)
postgres.pro.repeat 84ms(100)
postgres.pro.cache 17ms(100)
postgres.ef.repeat 1331ms(100)
MS Pro 202ms(100)
mssql.pro.repeat 114ms(100)
mssql.pro.cache 36ms(100)
sqlite.pro 166ms(100)
sqlite.pro.repeat 80ms(100)
sqlite.pro.cache 48ms(100)

Where - AND Condition

Example: Where with AND - filter by age and salary.
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .Where(e => e.Age >= 30 && e.Salary > 70000m)
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();
WhereQueryAND
PG Pro 115ms(100)
postgres.ef 1083ms(100)
postgres.pro.repeat 31ms(100)
postgres.pro.cache 18ms(100)
postgres.ef.repeat 1274ms(100)
MS Pro 144ms(100)
mssql.pro.repeat 136ms(100)
mssql.pro.cache 77ms(100)
sqlite.pro 135ms(100)
sqlite.pro.repeat 87ms(100)
sqlite.pro.cache 33ms(100)

Where - OR Condition

Filter employees using OR condition.
Find juniors (under 30) OR high earners (salary over 90k).
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .Where(e => e.Age < 30 || e.Salary > 90000m)
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();
WhereQueryOR
PG Pro 89ms(100)
postgres.ef 1066ms(100)
postgres.pro.repeat 49ms(100)
postgres.pro.cache 24ms(100)
postgres.ef.repeat 1277ms(100)
MS Pro 135ms(100)
mssql.pro.repeat 92ms(100)
mssql.pro.cache 69ms(100)
sqlite.pro 114ms(100)
sqlite.pro.repeat 73ms(100)
sqlite.pro.cache 24ms(100)

Where - NOT Equal

Filter employees using NOT equal condition.
Find all non-managers.
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .Where(e => e.Position != "Manager")
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();
WhereQueryNOT
PG Pro 148ms(100)
postgres.ef 1016ms(100)
postgres.pro.repeat 42ms(100)
postgres.pro.cache 21ms(100)
postgres.ef.repeat 1281ms(100)
MS Pro 130ms(100)
mssql.pro.repeat 71ms(100)
mssql.pro.cache 39ms(100)
sqlite.pro 114ms(100)
sqlite.pro.repeat 65ms(100)
sqlite.pro.cache 24ms(100)

Where - Range Condition

Filter employees by salary range.
Find mid-level earners between 60k and 90k.
var sw = Stopwatch.StartNew();

var query = redb.Query<EmployeeProps>()
    .Where(e => e.Salary >= 60000m && e.Salary < 90000m)
    .Take(100);

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

var result = await query.ToListAsync();
sw.Stop();
WhereQueryRange
PG Pro 102ms(100)
postgres.ef 1076ms(100)
postgres.pro.repeat 40ms(100)
postgres.pro.cache 18ms(100)
postgres.ef.repeat 1008ms(100)
sqlite.pro 112ms(100)
sqlite.pro.repeat 77ms(100)
sqlite.pro.cache 12ms(100)
MS Pro 169ms(100)
mssql.pro.repeat 61ms(100)
mssql.pro.cache 35ms(100)