RecursiveAI.OneLite.Data is an ADO.NET provider: OneLiteConnection is a DbConnection, so code written against DbConnection, DbCommand, DbParameter and DbDataReader runs on a SQLite file with no native library.
dotnet new console -o AdoNetSqlite && cd AdoNetSqlite dotnet add package RecursiveAI.OneLite.Data --version 0.2.0-preview.2
RecursiveAI.OneLite.Data brings the core package, RecursiveAI.OneLite, with it. Put this in Program.cs and run dotnet run:
using System.Data.Common; using RecursiveAI.OneLite.Data; await using DbConnection conn = new OneLiteConnection("Data Source=contacts.db"); await conn.OpenAsync(); await using (DbCommand create = conn.CreateCommand()) { create.CommandText = """ create table if not exists contacts (id integer primary key, name text not null, email text) """; await create.ExecuteNonQueryAsync(); } await using (DbCommand insert = conn.CreateCommand()) { insert.CommandText = "insert into contacts (name, email) values (@name, @email)"; DbParameter name = insert.CreateParameter(), email = insert.CreateParameter(); name.ParameterName = "@name"; email.ParameterName = "@email"; insert.Parameters.Add(name); insert.Parameters.Add(email); var rows = new[] { ("Ada", "[email protected]"), ("Grace", null), ("Linus", "[email protected]") }; foreach ((string n, string? e) in rows) { name.Value = n; email.Value = (object?)e ?? DBNull.Value; // null is DBNull.Value in ADO.NET Console.WriteLine($"inserted {await insert.ExecuteNonQueryAsync()} row"); } } await using (DbCommand query = conn.CreateCommand()) { query.CommandText = "select id, name, email from contacts where name like @p order by id"; query.Parameters.Add(new OneLiteParameter("@p", "%a%")); await using DbDataReader r = await query.ExecuteReaderAsync(); while (await r.ReadAsync()) Console.WriteLine($"{r.GetInt64(0)} {r.GetString(1)} " + (r.IsDBNull(2) ? "(no email)" : r.GetString(2))); } await using (DbCommand count = conn.CreateCommand()) { count.CommandText = "select count(*) from contacts"; Console.WriteLine($"{(long)(await count.ExecuteScalarAsync())!} contacts"); }
inserted 1 row inserted 1 row inserted 1 row 1 Ada [email protected] 2 Grace (no email) 3 contacts
- Journal mode.
OneLiteConnectionkeeps SQLite’s default, a rollback journal (delete), as the check below shows.pragma journal_mode=walswitches a file to WAL; the mode is stored in the file, so later connections open it in WAL mode too. - Parameters.
@x,:xand$xin the SQL match a parameter namedx, with or without its prefix. A plain?takes the parameter at its position. AnullValuethrows: useDBNull.Value. - Types.
GetValuereturnslong,double,string,byte[]orDBNull.Guidbinds as upper-case text,DateTimeasyyyy-MM-dd HH:mm:ss.FFFFFFF,decimalas invariant text. - Real async. Every
…Asyncmethod awaits the storage layer; over a fully cached database it completes synchronously. - Connection string.
Data Sourceis a path or:memory:.Mode=ReadOnlyopens read-only,Mode=ReadWriterequires the file to exist, andDefault Timeoutis the seconds to wait for a lock (default 30).Poolingis accepted and ignored. - In the project’s words, “Dapper and other
DbConnectionconsumers work unchanged”.
Check it with sqlite3
The sqlite3 shell (3.53.4, from sqlite.org) reads the file the provider wrote, in rollback-journal mode, with the NULL email intact:
sqlite3 contacts.db "select id, name, ifnull(email, 'NULL') from contacts" \ "pragma journal_mode" "pragma integrity_check"
1|Ada|[email protected] 2|Grace|NULL 3|Linus|[email protected] delete ok
Limits
- A preview: APIs may change between previews.
- It is OneLite’s own provider.
Microsoft.Data.Sqliteand EF Core can’t run on OneLite yet: in the README’s words, “theMicrosoft.Data.Sqlite/EF Core adapter” is “still to come before 1.0”. GetSchemaandCommandType.StoredProcedureare not supported;GetSchemaTableis minimal.GetFieldTypedescribes the current value’s storage class, not the declared column type.Cancel()does nothing: cancel an…Asynccall with its token.