Sound familiar?

Real problems. Real solutions. See if this is you.

Your database has 1000 tables. Nobody knows what half of them do.

Every entity spawns 5-10 tables. Orders, OrderItems, OrderStatuses, OrderHistory, OrderMetadata... New developer joins — takes a month to understand the schema. Change one field — update migrations, DAL, DTOs, mappers. Repeat for every microservice.

With RedBase

Two tables. That's it. Your business classes map directly. Order with nested Items, Customer, History — one C# class, one save, one query.

You have an entire project just for data access.

Repositories. Unit of Work. DTOs. AutoMapper profiles. Every change touches 5 files minimum. Half your codebase exists just to move data between layers that shouldn't exist.

With RedBase

Delete the DAL project. Work with business objects directly. Save what you have. Query what you need. Less code = less bugs.

Your junior doesn't know SQL. But the project needs a database.

Foreign keys, indexes, normalization, migration scripts... They just want to build features, not become a DBA. But every tutorial assumes you know what a clustered index is.

With RedBase

Write C# classes. Save objects. Query with LINQ. RedBase handles the SQL. Focus on business logic, not database internals.

Migrations broke production. Again.

Someone added a non-nullable column without a default. Rollback script doesn't work. It's 2 AM and you're manually fixing data on production while the PM watches.

With RedBase

No migrations. Add a property to your class — it just appears. Remove a property — old data stays, no crashes. Schema evolves with your code.

One entity. 30 tables. 500ms per query.

You need to load a complete Order: customer info, line items, products, categories, discounts, shipping, payment history... The SQL function is 150 lines. It runs half a second. And you need to query thousands of them.

Now multiply this. You have Orders, Invoices, Projects, Contracts — each with its own 30-table assembly function. Each maintained separately. Each breaks when schema changes.

Add a new field? Migration + update assembly function + rebuild indexes. Need an array property (Tags, Attachments)? New table + FK + index + update ALL queries that touch this entity. Every change cascades through the entire stack.

Traditional
SELECT o.*, c.*, i.*, p.*, cat.*, d.*, s.*, ph.*
FROM Orders o
LEFT JOIN Customers c ON ...
LEFT JOIN OrderItems i ON ...
LEFT JOIN Products p ON ...
LEFT JOIN Categories cat ON ...
LEFT JOIN Discounts d ON ...
LEFT JOIN Shipping s ON ...
LEFT JOIN PaymentHistory ph ON ...
WHERE o.Id = @id
-- 150 lines, 30 JOINs, 500ms
RedBase
var order = await redb.LoadAsync<Order>(id);
// Single key lookup, one data dump
// Parallel object assembly in memory
// Zero JOINs. 15ms.
Why can't others do this?

EF Core — uses JOINs (one massive query) or Split Queries (multiple roundtrips). Sequential object assembly.

Dapper — you write the SQL yourself. You build the JOINs. You assemble objects manually.

MongoDB — documents load fast, but: no ACID by default, no FK integrity, no SQL at all (own query language), limited LINQ support.

RedBase — relational database (PostgreSQL/SQL Server) with document-style loading. One key lookup, parallel assembly, full LINQ, ACID transactions. Best of both worlds.

Saving one object requires INSERT into 30 tables. In the right order.

Foreign key constraints demand correct sequence. Transaction management. Error handling for each insert. 500 lines of code just to save one business entity. And when you need to save 1000 at once? Performance nightmare.

Traditional
await _transaction.BeginAsync();
try {
    var customerId = await InsertCustomer(order.Customer);
    var orderId = await InsertOrder(order, customerId);
    foreach (var item in order.Items) {
        var productId = await InsertProduct(item.Product);
        await InsertOrderItem(orderId, productId, item);
        // ... 30 more inserts in correct order
    }
    await _transaction.CommitAsync();
} catch {
    await _transaction.RollbackAsync();
    throw;
}
RedBase
// One object
await redb.SaveAsync(order);

// 1000 objects — batch with BulkInsert
await redb.SaveAsync(orders);

Entity → DTO → ViewModel → Response. Four classes for one thing.

Database entity. Business DTO. API response model. Frontend ViewModel. AutoMapper profiles for each direction. Change one field — update four classes and three mappings. Half your bugs are mapping bugs.

With RedBase

One class. Business object = database entity = API response. No mapping. No AutoMapper. No DTO hell. Just your domain model.

MVP takes a month. Just for the database schema.

Stakeholders want to see something. But first you need to design tables, write migrations, set up the DAL, create repositories... By the time you have a working prototype, requirements changed.

With RedBase

Morning: define classes. Evening: working prototype. Schema grows with your code. Ship fast, iterate faster.

Client wants one new field. It takes a week.

Ticket to DBA. Migration script. Code review. Staging deployment. Production deployment window. Rollback plan. All for one nullable string field.

With RedBase

Add property to class. Commit. Deploy. Done. Old objects still work. New objects have the field. Five minutes.

Legacy system with 500 stored procedures. Can't rewrite, can't extend.

The old system works. Barely. Nobody wants to touch it. But new features need new data structures. Adding tables to the legacy schema is terrifying.

With RedBase

Add RedBase alongside. New features use new approach. Gradually migrate when ready. No big bang rewrite needed.

Products need different fields. One table per type? Or 1:1 joins everywhere?

Clothing needs Size and Color. Electronics needs Memory and Processor. Traditional approach: base Products table + ClothingDetails + ElectronicsDetails with 1:1 FK. Or one giant table with 100 nullable columns. Both are maintenance nightmares.

Traditional
-- Base table
CREATE TABLE Products (Id, Name, Price)

-- 1:1 extension tables
CREATE TABLE ClothingDetails (ProductId FK, Size, Color)
CREATE TABLE ElectronicsDetails (ProductId FK, Memory, CPU)

-- Every query needs JOINs
SELECT p.*, c.*, e.* FROM Products p
LEFT JOIN ClothingDetails c ON ...
LEFT JOIN ElectronicsDetails e ON ...
RedBase
// Just use C# inheritance
[RedbScheme] public class Product { ... }
[RedbScheme] public class Clothing : Product 
{ 
    public string Size { get; set; }
    public string Color { get; set; }
}
[RedbScheme] public class Electronics : Product 
{ 
    public int Memory { get; set; }
}

// Query polymorphically
var clothes = await redb.Query<Clothing>().ToListAsync();

You need a List in your entity. Here comes another table.

Order has Items. User has Roles. Product has Tags. Every collection = new table + foreign key + index + JOIN on every query. Even for 3-5 items. Some developers give up and store JSON strings. Or comma-separated values. Complete loss of typing and querying.

Traditional
// New table: OrderItems
// New table: UserRoles  
// New table: ProductTags
// FK constraints, indexes, migrations...

var order = await context.Orders
    .Include(o => o.Items)
    .Include(o => o.Tags)
    .FirstAsync(o => o.Id == id);

// Or the hack:
public string TagsJson { get; set; } // lose typing
public string RolesCsv { get; set; } // can't query
RedBase
[RedbScheme]
public class Order
{
    public List<OrderItem> Items { get; set; }
    public List<string> Tags { get; set; }
    public Dictionary<string, decimal> Prices { get; set; }
}

// Just save it. Just query it. Done.
await redb.SaveAsync(order);
var order = await redb.LoadAsync<Order>(id);

You need lookup tables. Cities, Colors, Materials. Each with FK, indexes, joins.

Countries → Cities table with FK. Colors table with FK. Materials table with FK. Now Cities needs more data — population, area, coordinates. Extend the lookup table? But Colors don't need that. Add nullable FK to a City entity? What about Materials that also need entity reference? Your lookup tables become a mess of optional FKs.

Traditional
-- Lookup tables
CREATE TABLE Cities (Id, Name, CountryId FK)
CREATE TABLE Colors (Id, Name, Hex)
CREATE TABLE Materials (Id, Name)

-- Oh wait, Cities need more data...
ALTER TABLE Cities ADD Population, Area, ...
-- Or reference a separate entity?
ALTER TABLE Cities ADD CityEntityId FK NULL
-- Materials too? Another FK column...

-- Filter by lookup?
WHERE CityId IN (SELECT Id FROM Cities WHERE ...)
RedBase
// Built-in ListItem support
[RedbScheme]
public class Order
{
    public RedbListItem Status { get; set; }
    public RedbListItem City { get; set; }
    public List<RedbListItem> Tags { get; set; }
}

// Filter by value
.Where(p => p.Status.Value == "Active")

// WhereIn
.WhereIn(p => p.Status.Value, ["Active", "Pending"])

// GroupBy works too!
.GroupBy(p => p.City.Value)

Bottom line: ship faster, stress less.

Development is 2x faster. Maybe 3x. You're not fighting schemas, migrations, mappings. You're building business logic. Delivering features. Shipping products.

Give your client working business processes — not database headaches.

Your whole team benefits.

Business Analyst — speaks business entities directly. No need to translate to database schemas. They describe Order, Customer, Product — you code Order, Customer, Product. Same language.

QA Engineer — tests business logic, not table relationships. "Create Order with 3 items" not "INSERT into 5 tables in correct sequence".

Documentation — no more volumes of database diagrams. BA documents business entities. You add technical notes where needed. That's it.

But wait — what if they ask about the database?

It's right there. Standard PostgreSQL or SQL Server. Full access. Want custom stored procedures? Go ahead. Need raw SQL for a complex report? No problem. Want to extend beyond RedBase for something exotic? Nothing stops you.

Start simple. Scale complex. Your business logic drives the architecture — not the other way around.

2-3x Faster Development
0 Migrations to Debug
100% SQL Access When Needed

Your data has hierarchy. Then different entity types appear at different levels. Now what?

Imagine a large corporation. Multiple headquarters. Regional offices under each HQ. Departments in every office. Warehouses under departments. Retail points. Showrooms. Products in warehouses. And then... a product gets transferred to HQ for review.

Wait — the product was in a warehouse, but now it's in... a showroom? At headquarters? And tomorrow it might be at a retail point in another city?

How do you model THIS in your database?

Traditional

-- The "Enterprise Hierarchy" nightmare
-- 
-- First, tables for each level:
CREATE TABLE Headquarters (id, name, ...);
CREATE TABLE RegionalOffices (id, hq_id, name, ...);
CREATE TABLE Departments (id, office_id, name, ...);
CREATE TABLE Warehouses (id, department_id, ...);
CREATE TABLE RetailPoints (id, department_id, ...);
CREATE TABLE Showrooms (id, office_id, ...);
CREATE TABLE Products (id, name, sku, ...);

-- Now the fun part: WHERE is the product?
-- Option A: Multiple FK columns
ALTER TABLE Products ADD warehouse_id INT NULL;
ALTER TABLE Products ADD retail_point_id INT NULL;
ALTER TABLE Products ADD showroom_id INT NULL;
ALTER TABLE Products ADD hq_showroom_id INT NULL;
-- Only ONE should be non-null. Good luck enforcing that.

-- Option B: Polymorphic association (the "Rails way")
ALTER TABLE Products ADD location_type VARCHAR(50);
ALTER TABLE Products ADD location_id INT;
-- No FK constraint possible. Orphaned records guaranteed.

-- Option C: Junction tables for each combination
CREATE TABLE Product_Warehouse (product_id, warehouse_id);
CREATE TABLE Product_RetailPoint (product_id, point_id);
CREATE TABLE Product_Showroom (product_id, showroom_id);
-- Now track history of movements...
CREATE TABLE ProductMovementHistory (...);

-- Query: "All products under London HQ (any level)"
-- 250 lines of recursive CTEs across 7 tables
-- Performance: 3-5 seconds on 100K products

-- Move product to different location?
-- BEGIN TRANSACTION;
-- DELETE FROM Product_Warehouse WHERE...
-- INSERT INTO Product_Showroom VALUES...
-- INSERT INTO ProductMovementHistory...
-- COMMIT;
-- Hope you got the order right!

RedBase

// Define your entities - just C# classes with attribute
[RedbScheme] public class Headquarters { ... }
[RedbScheme] public class Office { ... }
[RedbScheme] public class Department { ... }
[RedbScheme] public class Warehouse { ... }
[RedbScheme] public class Showroom { ... }
[RedbScheme] public class Product { ... }

// Create hierarchy - any entity under any parent
await redb.CreateChildAsync(product, warehouse);
// Product is now under Warehouse. Done.

// Move product to HQ showroom? One line:
await redb.MoveObjectAsync(product, hqShowroom);
// History tracked automatically. Done.

// Find ALL products under London HQ (any depth)
var products = await redb
    .TreeQuery<Product>(londonHQ.Id, maxDepth: 10)
    .Where(p => p.InStock)
    .ToListAsync();
// All products from all warehouses, showrooms,
// retail points under London. 15ms.

// Find all departments at level 3
var level3 = await redb
    .TreeQuery<Department>(hq.Id)
    .WhereLevel(3)
    .ToListAsync();

// Build complete tree with all children loaded
var tree = await redb
    .LoadTreeAsync<Headquarters>(hq, maxDepth: 5);
// Navigate: tree.Children[0].Children[1].Props

The secret: In RedBase, hierarchy is built-in. Every object has a parent. Different entity types can live at any level. A Product can be under a Warehouse today and under a Showroom tomorrow — same API, same table, zero schema changes.

But wait, it gets worse...

The MacBook is on a warehouse shelf. Before shipping to HQ for executive review, someone puts an accessory kit inside the box. A cable, a case, maybe AirPods — for the boss.

Now the box travels to HQ. At HQ, they might take out the accessory and keep it there. Or the whole package goes to retail — MacBook with accessory still attached.

In traditional DB: How do you model "accessory inside a box inside a warehouse"? Then move the box? Then optionally detach the accessory? I smell bugs. Lots of them.

Traditional

-- Accessory inside box? More junction tables!
CREATE TABLE ProductContents (
    container_id INT,
    content_id INT,
    added_date DATETIME,
    -- Wait, what if content has its own contents?
    -- Recursive nightmare begins...
);

-- Move box to HQ
UPDATE Products SET location_id = @hqId 
WHERE id = @boxId;
-- OOPS! Accessory still points to warehouse!
-- Manual fix required:
UPDATE Products SET location_id = @hqId 
WHERE id IN (SELECT content_id FROM ProductContents 
             WHERE container_id = @boxId);
-- What about nested contents? More recursion...

-- Detach accessory at HQ?
DELETE FROM ProductContents 
WHERE container_id = @boxId AND content_id = @accId;
UPDATE Products SET location_id = @hqId 
WHERE id = @accId;
-- History? Audit trail? Good luck.

RedBase

// Put accessory inside MacBook box
await redb.CreateChildAsync(accessory, macbookBox);
// Done. Accessory is now INSIDE the box.

// Move entire box to HQ
await redb.MoveObjectAsync(macbookBox, hqShowroom);
// Accessory travels WITH the box. Automatically.
// No extra code. No forgotten updates.

// Take accessory out, leave at HQ
await redb.MoveObjectAsync(accessory, hqShowroom);
// Accessory now directly under HQ.
// MacBook box can go to retail alone.

// Query: What's inside any container?
var contents = await redb
    .TreeQuery<Product>(macbookBox.Id)
    .ToListAsync();

// Full audit: where was accessory?
var path = await redb
    .GetPathToRootAsync<Product>(accessory);
// [Accessory] → [Box] → [Showroom] → [HQ]

The point: Parent-child relationships are automatic. Move a parent — children follow. Detach a child — it stays where you put it. Query any level. No orphans. No forgotten updates. No bugs at 2 AM.

Keep growing. Keep nesting. Still just a few lines.

Tomorrow the business says: "We need to track which employee packed each box." Add a property. Done.

Next week: "Boxes can contain other boxes." Already works. Any object can be a parent.

Next month: "We need shipment containers that hold multiple boxes from different warehouses, going to different destinations, with customs documents attached." CreateChildAsync. MoveObjectAsync. TreeQuery. Same API. Zero migrations.

Your competitors are still writing ALTER TABLE scripts. You're shipping features.

But wait... objects don't just have parents. They reference OTHER objects.

An Order references a Customer. And multiple Products. And a Shipping address. And Payment history. And Discount coupons. And a Sales manager who made the deal.

Oh, and the Customer has their own Address. And a list of previous Orders (circular!). And the Products have Categories. And Suppliers. And Reviews from other Customers.

Now save all of this. In the right order. With correct FKs. Without orphans. Without deadlocks. Welcome to Graph Hell.

Traditional

-- Order references Customer, Products, Payments...
CREATE TABLE Orders (
    id INT PRIMARY KEY,
    customer_id INT REFERENCES Customers(id),
    shipping_address_id INT REFERENCES Addresses(id),
    sales_manager_id INT REFERENCES Employees(id),
    discount_id INT NULL REFERENCES Discounts(id),
    -- Wait, what about multiple products?
);

-- Junction table for Order-Product many-to-many
CREATE TABLE OrderProducts (
    order_id INT REFERENCES Orders(id),
    product_id INT REFERENCES Products(id),
    quantity INT,
    price_at_order DECIMAL,
    PRIMARY KEY (order_id, product_id)
);

-- Another junction for payment history
CREATE TABLE OrderPayments (
    order_id INT REFERENCES Orders(id),
    payment_id INT REFERENCES Payments(id),
    -- ...
);

-- Save order? Here's the nightmare:
BEGIN TRANSACTION;
-- 1. Ensure Customer exists (or create)
-- 2. Ensure Address exists (or create)  
-- 3. INSERT Order (need customer_id first!)
-- 4. INSERT OrderProducts for each product
-- 5. INSERT Payments
-- 6. INSERT OrderPayments links
-- 7. UPDATE Order with payment totals
-- Hope nothing failed in between!
COMMIT;

-- Delete order? CASCADE horror awaits.
-- Load order with all relations? 15 JOINs.

RedBase

// Just declare your business model
[RedbScheme]
public class OrderProps
{
    public Customer Customer { get; set; }
    public Address ShippingAddress { get; set; }
    public Employee SalesManager { get; set; }
    
    // Array of objects? Just declare it.
    public Product[] Products { get; set; }
    
    // Array of RedbObjects with their own Props!
    public RedbObject<PaymentProps>[] Payments { get; set; }
    
    // Dictionary of objects? Why not!
    public Dictionary<string, RedbObject<CouponProps>> 
        Coupons { get; set; }
}

// Save entire object graph
var order = new RedbObject<OrderProps>
{
    Props = new OrderProps
    {
        Customer = customer,
        Products = new[] { laptop, mouse, keyboard },
        Payments = new[] { payment1, payment2 },
        Coupons = new Dictionary<string, ...>
        {
            ["SUMMER20"] = summerDiscount
        }
    }
};

await redb.SaveAsync(order);
// ALL nested objects saved. All links created.
// One line. One transaction. Done.

// Load with full graph
var loaded = await redb.LoadAsync<OrderProps>(id);
// loaded.Props.Customer - ready
// loaded.Props.Products[0] - ready
// loaded.Props.Payments[1].Props - ready
// loaded.Props.Coupons["SUMMER20"] - ready

Object graphs are first-class citizens. Declare references, arrays, dictionaries of objects in your Props. Save once — the entire graph persists. Load once — the entire graph materializes. No junction tables. No FK nightmares. No orphans. Ever.

Customer Customer
Single object reference
Product[] Products
Array of business classes
RedbObject<T>[] Items
Array of full objects with IDs
Dictionary<K, T>
Keyed collections of anything

"Delete... but not really. And show me progress."

Soft delete. Add `is_deleted` to every table. Remember to filter it in EVERY query. Forget once — users see deleted data. Or worse, deleted data corrupts reports.

Need to actually purge? Manual scripts. Batched deletes. "How many left?" — nobody knows until it's done. Or crashes.

Traditional

-- Add is_deleted EVERYWHERE
ALTER TABLE Orders ADD is_deleted BIT DEFAULT 0;
ALTER TABLE Customers ADD is_deleted BIT DEFAULT 0;
ALTER TABLE Products ADD is_deleted BIT DEFAULT 0;
-- ... 50 more tables

-- EVERY query needs this:
SELECT * FROM Orders 
WHERE is_deleted = 0  -- Forget this = bug
  AND customer_id IN (
    SELECT id FROM Customers WHERE is_deleted = 0
  );

-- Purge? Good luck:
DECLARE @batch INT = 1000;
WHILE 1=1 BEGIN
    DELETE TOP(@batch) FROM Orders 
    WHERE is_deleted = 1;
    IF @ROWCOUNT = 0 BREAK;
    -- Progress? What progress?
END

RedBase

// Option 1: Sync delete with real-time progress
var progress = new Progress<PurgeProgress>(p =>
{
    var pct = p.Total > 0 ? p.Deleted * 100 / p.Total : 0;
    Console.WriteLine($"{p.Deleted}/{p.Total} ({pct}%)");
});

await redb.DeleteWithPurgeAsync(ids, batchSize: 100, progress);
// Real-time progress callback. Batched. Safe.

// Option 2: Fire-and-forget background service
var mark = await deletionService.DeleteAsync(ids, user);
// Returns IMMEDIATELY. Purge runs in background.

// Check progress anytime:
var status = await deletionService.GetProgressAsync(mark.TrashId);
// status.Deleted, status.Remaining, status.Status

// Query() auto-filters deleted objects
var orders = await redb.Query<Order>().ToListAsync();
// Only active. No is_deleted. Ever.

// Cluster-safe: survives restarts!

Fire and forget. Objects move to trash container, background service purges in batches. Progress stored in DB — survives restarts. Cluster-safe. No `is_deleted` pollution. Query() filters automatically.

"Find orders where Customer.Address.City = 'London'"

Nested property search. In traditional ORM: 5 JOINs. 50 lines of SQL. Forgot an Include? Runtime error. Changed property name? Find all 47 places.

Traditional

SELECT o.* 
FROM Orders o
JOIN Customers c ON o.customer_id = c.id
JOIN Addresses a ON c.address_id = a.id
JOIN Cities ct ON a.city_id = ct.id
WHERE ct.name = 'London'
  AND o.is_deleted = 0
  AND c.is_deleted = 0
  AND a.is_deleted = 0;
-- Forgot a JOIN? Wrong results.
-- Renamed City to Location? Find all queries.

RedBase

var orders = await redb.Query<Order>()
    .Where(o => o.Customer.Address.City == "London")
    .ToListAsync();
// One line. Type-safe. Refactor-friendly.
// Rename City? Compiler catches ALL usages.

"N+1 again. Third time this sprint."

Forgot an Include? Loop loads 1000 customers one by one. Performance tanks. Add Include — now you load too much data. Balance between eager and lazy loading is a constant battle.

Traditional ORM

var orders = await context.Orders
    .Include(o => o.Customer)      // forgot this = N+1
    .Include(o => o.Items)
        .ThenInclude(i => i.Product)  // and this
    .Where(o => o.Status == "Active")
    .ToListAsync();
// Miss one Include = performance bug
// Add too many = memory bloat

RedBase

var orders = await redb.Query<Order>()
    .Where(o => o.Status == "Active")
    .ToListAsync();
// Full object graph. Always. One query.
// No Include chains. No N+1. By design.

"Who changed this? When? What was the value before?"

Audit trail. Build audit tables. Write triggers. 500 lines of boilerplate. "Show me history of this order" — custom code for every entity.

Traditional

-- Audit table for EVERY entity
CREATE TABLE Orders_Audit (
    id INT, order_id INT,
    changed_by INT, changed_at DATETIME,
    old_value NVARCHAR(MAX),
    new_value NVARCHAR(MAX)
);

-- Trigger for EVERY table
CREATE TRIGGER tr_Orders_Audit ON Orders
AFTER UPDATE AS BEGIN
    INSERT INTO Orders_Audit (...)
    SELECT ... FROM inserted JOIN deleted ...
END;
-- Repeat x50 tables. Maintain forever.

RedBase

// Built into EVERY object:
order.DateCreate   // When created
order.DateModify   // Last modified
order.WhoChangeId  // User who changed it
order.OwnerId      // Object owner

// Query by modification:
var recentChanges = await redb.Query<Order>()
    .WhereRedb(o => o.DateModify > yesterday)
    .ToListAsync();

// No triggers. No audit tables.
// Metadata is part of every RedbObject.

"This is MY object. Only I should see it."

Object ownership. Build permissions table. FK everywhere. Complex JOINs in every query. Forget once — data leak.

Traditional

-- Permissions table
CREATE TABLE ObjectPermissions (
    object_id INT, object_type VARCHAR(50),
    user_id INT, permission VARCHAR(20)
);

-- EVERY query needs this:
SELECT o.* FROM Orders o
JOIN ObjectPermissions p 
  ON p.object_id = o.id 
  AND p.object_type = 'Order'
WHERE p.user_id = @currentUser
  AND p.permission = 'read';
-- Polymorphic FK. No referential integrity.
-- Forget the JOIN = security hole.

RedBase

// owner_id is built into every RedbObject
var order = new RedbObject<OrderProps>
{
    OwnerId = currentUser.Id,
    Props = new OrderProps { ... }
};

// Filter by owner:
var myOrders = await redb.Query<Order>()
    .WhereRedb(o => o.OwnerId == currentUser.Id)
    .ToListAsync();

// Built-in. Type-safe. No extra tables.

"Update 100,000 objects. Without timeout. With progress."

Bulk operations. Batch manually. Handle timeouts. Track progress yourself. Memory spikes. Connection pool exhaustion. Friday 5pm surprises.

Traditional

// Manual batching nightmare
var allItems = await GetAllItems(); // Memory!
var batches = allItems.Chunk(1000);
var processed = 0;

foreach (var batch in batches)
{
    using var tx = BeginTransaction();
    foreach (var item in batch)
    {
        item.Status = "Processed";
        await context.SaveChangesAsync(); // Slow!
    }
    tx.Commit();
    processed += batch.Length;
    // Progress? Console.WriteLine I guess...
}
// Timeout? Retry from scratch.

RedBase Pro

// Bulk save with parallel processing
var items = await redb.Query<Item>().ToListAsync();

// Modify in parallel
Parallel.ForEach(items, item => 
{
    item.Props.Status = "Processed";
});

// Bulk save - optimized batches
await redb.SaveAsync(items);
// Internal: BulkInsert, BulkUpdate, transactions.

// Delete with real-time progress:
var progress = new Progress<PurgeProgress>(p =>
    Console.WriteLine($"{p.Deleted}/{p.Total}"));

await redb.DeleteWithPurgeAsync(ids, 
    batchSize: 100, progress);
// Background purge. Cluster-safe.

The Big Picture: You're Not Just Building an App

With hierarchies, polymorphic trees, object graphs, and full LINQ — you're not writing another business solution. You're building a platform.

A mini-CRM. A BPM engine. A no-code constructor. An IoT data hub. Whatever your domain — the database layer is solved. You focus on business logic. Ship features. Impress stakeholders.

Your expertise shines through — not buried under ORM plumbing. Your solutions stay elegant, maintainable, professional. Your product exceeds expectations — because you're not fighting the database anymore.

Add AI — and you have a next-gen platform. Structured data + strong typing + LINQ queries = perfect foundation for AI/ML integration.

Zero Exotic Risks

PostgreSQL / SQL Server — battle-tested, enterprise-grade
No exotic graph databases with 10-page setup guides
No untyped JSONB where bugs hide until production
No external NoSQL clusters to deploy and maintain
Cloud-ready — Azure, AWS, GCP, on-prem — your choice

Traditional database. Revolutionary productivity. That's the RedBase promise.

Enterprise hierarchy — different types at every level
Headquarters Headquarters
London Office Office
Berlin Office Office
Warehouse A Warehouse
Showroom Showroom
Retail Point RetailPoint
iPhone 15 Product
MacBook Pro Product Moving to HQ
// Move product between ANY locations
await redb.MoveObjectAsync(macbook, hqShowroom);
TreeQuery
Query any subtree with full LINQ support
MoveObjectAsync
Relocate objects between parents instantly
Polymorphic Hierarchy
Different entity types at any level
WhereLevel / WhereLeaves
Query by depth, find leaf nodes
GetPathToRoot
Build breadcrumbs instantly
Recursive CTE
Optimized SQL under the hood
WhereChildrenOf
Get direct children of any node
WhereRoots
Find all root nodes instantly
WhereHasAncestor<T>
Find by parent type condition
WhereHasDescendant<T>
Find by child type condition
ToRootListAsync
Load complete tree structure
GetDescendantsAsync
All descendants at any depth
SaveAsync(graph)
Save entire object graph at once
ChangeTracking
Auto-detect graph modifications
LoadAsync<T>
Full graph materialization

How RedBase Compares

Feature comparison with popular alternatives

Feature RedBase EF Core Dapper MongoDB JSONB PostgreSQL
Strong Typing ~ ~
Full LINQ Support ~
Zero Migrations
Nested Objects ~
Tree Queries (CTE)
Polymorphic Hierarchy ~ ~
Object Graphs ~ ~
Query Nested Props ~
ACID Transactions ~
Raw SQL Access
Bulk Operations ~ ~ ~
Built-in Audit Fields
Owner/Permissions
ListItem (Lookups)
Window Functions ~
No Schema per Entity ~
Foreign Keys
Aggregations (Sum, Avg...)
GroupBy
Change Tracking
Soft Delete + Background Purge
Built-in Users, Roles & Auth
SQL Preview (ToSqlString) ~
Computed Expressions ~ ~ ~
Indexes ~
Stored Procedures
Multi-DB Support
Dictionary<Tuple, T>
Export/Import (.redb) ~
Full support ~ Partial / Manual Not supported
EF Core — great ORM, but requires migrations, manual Include chains, no built-in tree queries
Dapper — fast micro-ORM, but raw SQL only, no object mapping or nested structures
MongoDB — flexible documents, but no ACID by default, different query language, no SQL power
JSONB — JSON in SQL, but lose typing, complex queries, index limitations
RedBase: Multi-DB — PostgreSQL or SQL Server, same code. Export to .redb file, import anywhere. Zero vendor lock-in.
RedBase: Trees — hierarchies built-in. TreeQuery, MoveObjectAsync, GetPathToRoot — no CTEs to write manually.
RedBase: Soft Delete — atomic mark + background purge as a built-in BackgroundService. Progress stored in DB (survives restarts). Cluster-safe: orphaned tasks are recovered with atomic claiming on startup. Query() auto-filters deleted objects — no is_deleted anywhere.
RedBase: Users & Auth — full user management out of the box. CreateUser, ValidateUser (SHA256+salt), Roles, Permissions (CRUD per object/scheme), SecurityContext, CreateSystemContext. No ASP.NET Identity dependency — works in console apps, services, Blazor.
RedBase: SQL Preview — call ToSqlString() on any query to see the exact SQL before execution. No logging setup, no interceptors. Copy to pgAdmin, run EXPLAIN ANALYZE. Full transparency.
We eat our own cooking This documentation site stores all its content — pages, examples, API references — as RedB objects in a MSSQL database. No CMS, no static files. Just RedBase.

Try it. 15 minutes to first query.

No schema design. No migrations. Define classes, save objects, query with LINQ.

2-3x faster development
0 migrations
1,024 free Pro requests/launch

Open Source core. Pro features with trial limit. Start free, scale when needed.