Quickstart
Define an entity, create a SQLite context, run a typed query and print the rows - the smallest end-to-end NextORM program.
Prerequisites: Installation.
Overview
A NextORM query always has four parts:
- an entity (a class or an interface) whose properties map to columns;
- a context (
IDataContext) built from a connection or a connection string; - a query built with
From,Select,Where, and so on; - a terminal such as
ToList,First,AnyorToAsyncEnumerablethat executes it.
From also registers T's metadata the first time it is seen. The context is disposable: a
context created from a connection string owns and closes the connection, while a context created from a
supplied DbConnection leaves that connection open.
Complete minimal program
The following program is self-contained. It creates an in-memory SQLite database, seeds one table,
builds a context with UseSqlite, and streams the rows back as an anonymous type.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.Data.Sqlite;
using NextORM.Core;
using NextORM.Sqlite;
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
using (var setup = connection.CreateCommand())
{
setup.CommandText =
"create table simple_entity (id integer primary key);" +
"insert into simple_entity (id) values (1), (2), (3);";
setup.ExecuteNonQuery();
}
var builder = new DataContextBuilder().UseSqlite(connection);
using var dataContext = builder.CreateDataContext();
await foreach (var row in dataContext.From<ISimpleEntity>()
.Select(entity => new { Id = (long)entity.Id })
.ToAsyncEnumerable())
{
Console.WriteLine($"Id = {row.Id}");
}
// The entity can live in its own file. In a single-file top-level program the type
// declaration must come after the top-level statements.
[SqlTable("simple_entity")]
public interface ISimpleEntity
{
[Key]
[Column("id")]
int Id { get; set; }
}
Output:
Id = 1
Id = 2
Id = 3
The generated SQL is a plain select over the mapped table and column:
select id
from simple_entity
Using a connection string
UseSqlite also accepts a file path (or an SQLite connection string fragment), and the context then
creates and owns the connection:
var builder = new DataContextBuilder().UseSqlite("app.db");
using var dataContext = builder.CreateDataContext();
Note: in
DEBUGbuildsUseSqlite(string filepath)throwsArgumentExceptionwhen the file does not exist. Pass:memory:or an existing path while developing; pass aDbConnectionto keep full control.UseSqlServer(connectionString)andUsePostgres(connectionString)have the corresponding string overloads.
Reading the data
The query above uses ToAsyncEnumerable. The other common terminals are:
| Terminal | Result |
|---|---|
ToList / ToListAsync |
List<TResult> |
First / FirstAsync |
the first row; throws if the sequence is empty |
FirstOrDefault / FirstOrDefaultAsync |
the first row or default |
Single / SingleOrDefault (and …Async) |
exactly one row (or default) |
Any / AnyAsync |
bool |
Count / CountAsync |
int |
Every terminal has a synchronous and an async form; prefer the async form in application code.
See also
- Entities and metadata - attributes, interface/class mapping, the
entity-free
TableAliasmode and fluent configuration. - Dependency injection - register the context instead of constructing it by hand.
Source: tests/nextorm.integration.tests/CommonTestSuite/Raw/CommonTestSuite.SqlCommand.cs:9,
tests/nextorm.integration.tests/CommonTestSuite/Raw/CommonTestSuite.SqlCommand.cs:20,
tests/nextorm.integration.tests/CommonTestSuite/Raw/CommonTestSuite.SqlCommand.cs:37;
tests/nextorm.integration.tests/Providers/TestProviders/SqliteTestProvider.cs:32;
tests/nextorm.sqlite.tests/Raw/ConnectionManagementTests.cs:115;
generated SQL: tests/nextorm.sqlite.tests/SqlGeneration/SqlGenerationTests.cs:111.