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; }
}
Array Contains - Multiple OR
Query employees with multiple skills using Array.Contains with OR. Find developers who know C# OR Python.
var sw = Stopwatch.StartNew();
// Pro: Multiple array Contains with OR
var query = redb.Query<EmployeeProps>()
.Where(e => e.Skills!.Contains("C#") || e.Skills!.Contains("Python"))
.Take(100);
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
ArrayContainsORPro
PG Pro92ms(100)
postgres.ef1051ms(100)
postgres.pro.repeat39ms(100)
postgres.pro.cache25ms(100)
postgres.ef.repeat1425ms(100)
MS Pro290ms(100)
mssql.pro.repeat243ms(100)
mssql.pro.cache191ms(100)
sqlite.pro63ms(100)
sqlite.pro.repeat12ms(100)
sqlite.pro.cache12ms(100)
Array NOT Contains
Query employees excluding specific skill. Find developers who don't have "intern" skill.
var sw = Stopwatch.StartNew();
// Pro: NOT Contains - exclude employees with specific skill
var query = redb.Query<EmployeeProps>()
.Where(e => e.Skills!.Contains("C#") && !e.Skills!.Contains("intern"))
.Take(100);
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
ArrayContainsNOTPro
PG Pro1578ms(100)
postgres.ef798ms(100)
postgres.pro.repeat186ms(100)
postgres.pro.cache135ms(100)
postgres.ef.repeat1577ms(100)
MS Pro189ms(100)
mssql.pro.repeat133ms(100)
mssql.pro.cache131ms(100)
sqlite.pro68ms(100)
sqlite.pro.repeat24ms(100)
sqlite.pro.cache14ms(100)
Array Contains + Conditions
Query employees combining array Contains with other conditions (Pro). Find senior C# developers over 30.
var sw = Stopwatch.StartNew();
// Pro: Combine array Contains with other conditions
var query = redb.Query<EmployeeProps>()
.Where(e => e.Skills!.Contains("C#") && 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();
ArrayContainsANDPro
PG Pro62ms(100)
postgres.ef802ms(100)
postgres.pro.repeat43ms(100)
postgres.pro.cache26ms(100)
postgres.ef.repeat1292ms(100)
MS Pro498ms(100)
mssql.pro.repeat824ms(100)
mssql.pro.cache768ms(100)
sqlite.pro238ms(100)
sqlite.pro.repeat229ms(100)
sqlite.pro.cache190ms(100)
Dict ContainsKey - Phone Directory
Query employees using Dictionary.ContainsKey. Find employees who have a desk phone.
var sw = Stopwatch.StartNew();
// Pro: Query dictionary with ContainsKey
var query = redb.Query<EmployeeProps>()
.Where(e => e.PhoneDirectory!.ContainsKey("desk"))
.Take(100);
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
DictionaryContainsKeyPro
PG Pro61ms(100)
postgres.ef777ms(100)
postgres.pro.repeat18ms(100)
postgres.pro.cache13ms(100)
postgres.ef.repeat1135ms(100)
MS Pro83ms(100)
mssql.pro.repeat48ms(100)
mssql.pro.cache46ms(100)
sqlite.pro58ms(100)
sqlite.pro.repeat11ms(100)
sqlite.pro.cache12ms(100)
Dict Indexer - Bonus by Year
Query employees using Dictionary indexer. Find employees with bonus over 6000 in 2023.
var sw = Stopwatch.StartNew();
// Pro: Query dictionary with indexer
var query = redb.Query<EmployeeProps>()
.Where(e => e.BonusByYear![2023] > 6000m)
.Take(100);
// Uncomment to see generated SQL:
//var sql = await query.ToSqlStringAsync();
//Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
DictionaryIndexerPro
PG Pro50ms(100)
postgres.ef786ms(100)
postgres.pro.repeat19ms(100)
postgres.pro.cache14ms(100)
postgres.ef.repeat1012ms(100)
MS Pro243ms(100)
mssql.pro.repeat105ms(100)
mssql.pro.cache85ms(100)
sqlite.pro60ms(100)
sqlite.pro.repeat54ms(100)
sqlite.pro.cache48ms(100)
Dict - Nested Class Property
Query employees using Dict indexer with nested class (Pro). Find employees whose HQ office is in specific city.
var sw = Stopwatch.StartNew();
// Pro: Dictionary<string, Address> with nested property access
var query = redb.Query<EmployeeProps>()
.Where(e => e.OfficeLocations!["HQ"].City == "New York")
.Take(100);
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
DictionaryNestedClassPro
PG Pro120ms(100)
postgres.ef1017ms(100)
postgres.pro.repeat69ms(100)
postgres.pro.cache64ms(100)
postgres.ef.repeat1082ms(100)
MS Pro113ms(100)
mssql.pro.repeat95ms(100)
mssql.pro.cache100ms(100)
sqlite.pro71ms(100)
sqlite.pro.repeat15ms(100)
sqlite.pro.cache13ms(100)
Dict - Tuple Key
Query employees using Dict with tuple key (Pro). Find employees with specific performance review.
var sw = Stopwatch.StartNew();
// Pro: Dictionary<(int Year, string Quarter), string> with tuple key
// Tuple key must be in a variable for Expression Tree to work
var reviewKey = (Year: 2024, Quarter: "Q1");
var query = redb.Query<EmployeeProps>()
.Where(e => e.PerformanceReviews![reviewKey] == "Excellent")
.Take(100);
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var result = await query.ToListAsync();
sw.Stop();
DictionaryTupleKeyPro
PG Pro52ms(100)
postgres.ef992ms(100)
postgres.pro.repeat28ms(100)
postgres.pro.cache25ms(100)
postgres.ef.repeat1136ms(100)
MS Pro81ms(100)
mssql.pro.repeat42ms(100)
mssql.pro.cache31ms(100)
sqlite.pro58ms(100)
sqlite.pro.repeat17ms(100)
sqlite.pro.cache15ms(100)
CountAsync - Total and Filtered
Count employees using CountAsync. Get total count and filtered count.
var sw = Stopwatch.StartNew();
// Total count
var totalCount = await redb.Query<EmployeeProps>().CountAsync();
// Filtered count
var managersCount = await redb.Query<EmployeeProps>()
.Where(e => e.Position == "Manager")
.CountAsync();
// Filtered by a field two nested classes deep
var highFloorCount = await redb.Query<EmployeeProps>()
.Where(e => e.HomeAddress!.Building!.Floor > 20)
.CountAsync();
sw.Stop();
CountAggregate
PG Pro258ms(101750)
postgres.ef292ms(101634)
postgres.pro.repeat104ms(101751)
postgres.pro.cache153ms(101752)
postgres.ef.repeat111ms(101635)
MS Pro202ms(100926)
mssql.pro.repeat122ms(100927)
mssql.pro.cache109ms(100928)
sqlite.pro59ms(102140)
sqlite.pro.repeat49ms(102141)
sqlite.pro.cache48ms(102142)
AnyAsync - Check Existence
Check if any employees exist using AnyAsync(). Returns true if at least one record exists.
var sw = Stopwatch.StartNew();
// Check if any employees exist (no predicate)
var query = redb.Query<EmployeeProps>();
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var anyExist = await query.AnyAsync();
// Existence within a set filtered by a field two nested classes deep
var anyOnTopFloor = await redb.Query<EmployeeProps>()
.Where(e => e.HomeAddress!.Building!.Floor == 30)
.AnyAsync();
sw.Stop();
AnyExistsIRedbQueryable.AnyAsync
PG Pro17ms(1)
postgres.ef17ms(1)
postgres.pro.repeat14ms(1)
postgres.pro.cache17ms(1)
postgres.ef.repeat10ms(1)
MS Pro20ms(1)
mssql.pro.repeat15ms(1)
mssql.pro.cache16ms(1)
sqlite.pro7ms(1)
sqlite.pro.repeat5ms(1)
sqlite.pro.cache4ms(1)
AnyAsync - With Predicate
Check if any employees match a condition using AnyAsync(predicate). More efficient than Where().AnyAsync() - stops at first match.
var sw = Stopwatch.StartNew();
// Check if any high earners exist (salary > 100k)
var query = redb.Query<EmployeeProps>();
// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);
var anyHighEarners = await query.AnyAsync(e => e.Salary > 100_000m);
// Predicate over a field one nested class deep and a field two nested classes deep
var anyBerlinHighFloor = await query.AnyAsync(e => e.HomeAddress!.City == "Berlin" && e.HomeAddress!.Building!.Floor > 25);
sw.Stop();