StrongDAO is a performant extension library for Microsoft Access DAO that brings modern, strongly-typed querying capabilities to your Access database applications.
- Strong Typing: Query your Access databases with full type safety using classes, records, tuples, or scalar values - no more brittle string-based field access
- Easy Migration Path: Designed as a drop-in enhancement for existing DAO applications. Start using it alongside your current code without a complete rewrite
- Dapper-Like Experience: Familiar query patterns similar to OLEDB + Dapper, but using native DAO objects - no need to mix DAO and OLEDB in the same codebase (they don't mix well!)
- Fast Performance: Up to 9x faster than traditional
DAO.OpenRecordset. Offering performance on par with Dapper in some scenarios - Dynamic Support: Full support for dynamic queries when you need flexibility, with results castable to
IDictionary<string, object>
Install Nuget package StrongDAO
Database.Query<T>Database.QueryFirstOrDefault<T>QueryDef.Query<T>QueryDef.QueryFirstOrDefault<T>
Replace your OpenRecordset calls with the methods above to get a strongly typed object (or list of object) out of your queries.
And their dynamic counterpart:
Database.QueryDatabase.QueryFirstOrDefaultQueryDef.QueryQueryDef.QueryFirstOrDefault
You can directly cast the results of those queries to (IDictionary<string, object?>) if you need a more dictionary-like way of accessing the data.
StrongRecordset
With the corresponding extension methods:
Database.OpenStrongRecordsetQueryDef.OpenStrongRecordset
Designed as a drop-in replacement for the built-in Recordset class and the OpenRecordset methods. This new recordset offers the same fonctionality as the original one, but it allows caching of the DAO.Fields (caching is activated by default). This can result in a free 2x performance boost. See the performance section for more details.
The StrongRecordset also includes the AsEnumerable() method which enables:
- foreach enumeration: Iterate through records using standard
foreachloops - LINQ support: Apply LINQ queries (e.g.,
Where,Select,OrderBy) directly on the recordset
// Example1: Replacing OpenRecordset with OpenStrongRecordset
_dbEngine = new DAO.DBEngine();
var _db = _dbEngine.CreateDatabase("test.mdb");
var rs = _db.OpenStrongRecordset("SELECT * FROM Users");
rs.Fields["Name"].Value = "StrongDAO";// Example2: Using AsEnumerable() with LINQ
_dbEngine = new DAO.DBEngine();
var _db = _dbEngine.CreateDatabase("test.mdb");
var rs = _db.OpenStrongRecordset("SELECT * FROM Users");
var results = rs.AsEnumerable()
.Where(r => (int)r.Fields["Age"].Value > 18)
.Select(r => new {
Id = r.Fields["ID"].Value,
Name = r.Fields["Name"].Value
})
.ToList();// Example 3: Doing strongly typed queries
// Class and record definition
public record PersonRecord(int? Id, string? PersonName, int? Age, string? Email, bool? IsActive, double? Score);
public class PersonClass
{
public int? Id { get; set; }
public string? PersonName { get; set; }
public int? Age { get; set; }
public string? Email { get; set; }
public bool? IsActive { get; set; }
public double? Score { get; set; }
}
_dbEngine = new DAO.DBEngine();
var _db = _dbEngine.CreateDatabase("test.mdb");
// Tuple (NOTE: Positional assignement for Tuples, names are useless)
var person = _db.Query<(int? Id, string? PersonName, int? Age, string? Email, bool? IsActive, double? Score)>("SELECT * FROM BenchmarkTable");
// Scalar
var id = _db.Query<int?>("SELECT Id FROM BenchmarkTable");
// Class
var personClass = _db.Query<PersonClass>("SELECT * FROM BenchmarkTable");
// Record
var personRecord = _db.Query<PersonRecord>("SELECT * FROM BenchmarkTable");
_db.Close();Benchmarks were conducted using BenchmarkDotNet on a AMD Ryzen 5 5600X system with .NET 10.0. All tests queried 50 rows from an Access database.
- Up to 9x faster than DAO.OpenRecordset
- 2x faster when using OpenStrongRecordset instead of OpenRecordset
- Comparable to Dapper when mapping to Tuple while providing native DAO integration
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| OLEDB_Dapper | 4.047 ms | 0.10 | 29.18 KB | 1.83 |
| StrongDAO_Tuple | 4.246 ms | 0.11 | 42.93 KB | 2.69 |
| DAO_GetRows | 4.612 ms | 0.12 | 21.23 KB | 1.33 |
| StrongDAO_Record | 8.372 ms | 0.22 | 47.22 KB | 2.96 |
| StrongDAO_Class | 8.443 ms | 0.22 | 47.77 KB | 3.00 |
| StrongDAO_QueryDef_Class | 8.741 ms | 0.22 | 47.77 KB | 3.00 |
| StrongDAO_OpenStrongRecordset | 18.385 ms | 0.47 | 16.8 KB | 1.05 |
| DAO_OpenRecordset (Baseline) | 38.893 ms | 1.00 | 15.93 KB | 1.00 |
Note: Ratios are relative to DAO_OpenRecordset (Baseline). Lower is better.