WitDatabase offers three distinct APIs for data access, each designed for different use cases and developer preferences. This guide helps you choose the right approach for your project.
Three Ways to Access WitDatabase
[[Svg Src="./witdatabase-full-architecture.svg" Alt="witdatabase-full-architecture"]]
Quick Comparison
| Aspect | Core API | ADO.NET | Entity Framework Core |
|---|---|---|---|
| Abstraction Level | Low | Medium | High |
| Query Language | None (Key-Value) | SQL | LINQ + SQL |
| Learning Curve | Medium | Low | Medium |
| Performance | Maximum | Excellent | Very Good |
| Type Safety | Manual | Manual | Automatic |
| Schema Management | Manual | Manual | Migrations |
| Change Tracking | No | No | Yes |
| Relationships | Manual | Manual (JOINs) | Navigation Properties |
| Best For | Caching, K/V stores | SQL queries, reports | Business applications |
When to Use Each Provider
Core API
Choose Core API when you need:
- Maximum possible performance
- Simple key-value storage
- Custom serialization format
- Direct control over transactions
- Caching layer implementation
- Document storage (JSON, MessagePack, Protobuf)
Typical use cases:
// Session storage
db.Put(sessionKey, sessionData);
var session = db.Get(sessionKey);
// Caching
db.Put(cacheKey, cachedResponse);
// Custom document store
db.Put(
Loading...
quot;user:{userId}".ToBytes(), JsonSerializer.SerializeToUtf8Bytes(user));
Package:
dotnet add package OutWit.Database.Core
ADO.NET Provider
Choose ADO.NET when you need:
- Full SQL capabilities
- Familiar DbConnection/DbCommand pattern
- Complex queries (JOINs, aggregations, CTEs)
- Direct control over SQL
- Integration with existing ADO.NET code
- Report generation
- Bulk data operations
Typical use cases:
// Complex reporting query
using var cmd = connection.CreateCommand();
cmd.CommandText = """
SELECT
c.Name AS Category,
COUNT(*) AS ProductCount,
AVG(p.Price) AS AvgPrice
FROM Products p
JOIN Categories c ON p.CategoryId = c.Id
GROUP BY c.Id
HAVING COUNT(*) > 5
ORDER BY AvgPrice DESC
""";
// Bulk insert with transaction
using var transaction = connection.BeginTransaction();
// ... insert thousands of records efficiently
transaction.Commit();
Package:
dotnet add package OutWit.Database.AdoNet
Entity Framework Core Provider
Choose EF Core when you need:
- Object-relational mapping (ORM)
- LINQ queries with IntelliSense
- Automatic change tracking
- Navigation properties and lazy loading
- Database migrations
- Integration with ASP.NET Core
- Rapid application development
- Strong typing and compile-time checks
Typical use cases:
// Business logic with relationships
var orders = await context.Orders
.Include(o => o.Customer)
.Include(o => o.Items)
.ThenInclude(i => i.Product)
.Where(o => o.Status == OrderStatus.Pending)
.OrderByDescending(o => o.CreatedAt)
.Take(10)
.ToListAsync();
// Automatic change tracking
var user = await context.Users.FindAsync(userId);
user.LastLoginAt = DateTime.UtcNow;
await context.SaveChangesAsync(); // UPDATE generated automatically
Package:
dotnet add package OutWit.Database.EntityFramework
Decision Flowchart
[[Svg Src="./witdatabase-api-decision-tree.svg" Alt="witdatabase-api-decision-tree"]]
Additional considerations:
If you...
Consider...
Are building a new application
EF Core — faster development
Have existing ADO.NET code
ADO.NET — minimal changes
Need maximum performance
Core API or ADO.NET
Want compile-time query validation
EF Core — LINQ
Need complex raw SQL
ADO.NET — full control
Are building a caching layer
Core API — minimal overhead
Need database migrations
EF Core — built-in
Work with legacy schemas
ADO.NET — no conventions
Mixing Providers
You can use multiple providers in the same application. They all work with the same underlying database file.
Example: EF Core + ADO.NET
Use EF Core for business logic and ADO.NET for complex reports:
public class OrderService
{
private readonly AppDbContext _context;
private readonly WitDbConnection _connection;
public OrderService(AppDbContext context, IConfiguration config)
{
_context = context;
_connection = new WitDbConnection(config.GetConnectionString("Default"));
_connection.Open();
}
// EF Core for business operations
public async Task<Order> CreateOrderAsync(OrderDto dto)
{
var order = new Order { /* ... */ };
_context.Orders.Add(order);
await _context.SaveChangesAsync();
return order;
}
// ADO.NET for complex reporting
public async Task<SalesReport> GetSalesReportAsync(DateTime from, DateTime to)
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = """
WITH MonthlySales AS (
SELECT
STRFTIME('%Y-%m', OrderDate) AS Month,
SUM(Total) AS Revenue,
COUNT(*) AS OrderCount
FROM Orders
WHERE OrderDate BETWEEN @from AND @to
GROUP BY STRFTIME('%Y-%m', OrderDate)
)
SELECT
Month,
Revenue,
OrderCount,
Revenue - LAG(Revenue) OVER (ORDER BY Month) AS Growth
FROM MonthlySales
ORDER BY Month
""";
cmd.Parameters.AddWithValue("@from", from);
cmd.Parameters.AddWithValue("@to", to);
// ... read and return report
}
}
Example: EF Core + Core API
Use EF Core for structured data and Core API for caching:
public class ProductService
{
private readonly AppDbContext _context;
private readonly WitDatabase _cache;
public ProductService(AppDbContext context)
{
_context = context;
_cache = new WitDatabaseBuilder()
.WithMemoryStorage()
.WithBTree()
.Build();
}
public async Task<Product?> GetProductAsync(int id)
{
// Check cache first
var cacheKey = Encoding.UTF8.GetBytes(
Loading...
quot;product:{id}");
var cached = _cache.Get(cacheKey);
if (cached != null)
return JsonSerializer.Deserialize<Product>(cached);
// Cache miss — load from database
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_cache.Put(cacheKey, JsonSerializer.SerializeToUtf8Bytes(product));
}
return product;
}
}
Performance Characteristics
Relative performance for common operations (lower is better):
[[Svg Src="./witdatabase-api-performance.svg" Alt="witdatabase-api-performance"]]
Note: EF Core overhead comes from change tracking, query translation, and object materialization. For most applications, this overhead is negligible compared to business logic execution time.
Feature Matrix
Feature
Core API
ADO.NET
EF Core
Data Access
Key-Value operations
✅
❌
❌
SQL queries
❌
✅
✅
LINQ queries
❌
❌
✅
Stored procedures
❌
❌
❌
Transactions
Basic transactions
✅
✅
✅
Savepoints
✅
✅
✅
Isolation levels
✅
✅
✅
Distributed transactions
❌
❌
❌
Schema
Auto-create tables
❌
❌
✅
Migrations
❌
❌
✅
Reverse engineering
❌
❌
❌
Advanced
Connection pooling
N/A
✅
✅
Async support
✅
✅
✅
Blazor WASM
✅
❌
❌
Batch operations
✅
✅
✅ (EF 7+)
Bulk update/delete
✅
✅
✅ (EF 7+)
Summary
Provider
One-Line Summary
Core API
Maximum performance, full control, no SQL — for caching and key-value storage
ADO.NET
Full SQL power with familiar API — for queries, reports, and existing codebases
EF Core
Modern ORM with LINQ — for business applications and rapid development