SqlBind.CSharp is a C# class library that is a wrapper for SQLite.

How to create a table and insert rows

Let's consider creating the Actors table as follows:

Actors

id name
1 Chloë Grace Moretz
2 Gary Carr
3 Jack Reynor

Create the following class to represent this table:

[Table("Actors")]
public record class Actor(
    [Column("id")][PrimaryKey][AutoIncrement] long Id,
    [Column("name")] string Name)
{
}

Each parameter in the constructor of the Actor class corresponds to each column in the Actors table in the same order. The type of each parameter must be either long or string.

Note that you can implement the Actor class without a record class. However, the parameter names of the constructor must start with an uppercase letter if you create a regular one according to the naming conventions of the record class. This is inconsistent with general naming conventions. Therefore, we recommend that you use record classes.

The following code from the Example class uses the Actor class to create the Actors table and add three rows of data to the table:

public sealed class Example
{
    private TransactionKit Kit { get; } = new TransactionKit(
        "example.db",
        m => Console.WriteLine(m()));

    public void CreateTableAndInsertRows()
    {
        Kit.Execute(q =>
        {
            q.NewTables(typeof(Actor));
            q.Insert(new Actor(0, "Chloë Grace Moretz"));
            q.Insert(new Actor(0, "Gary Carr"));
            q.Insert(new Actor(0, "Jack Reynor"));
        });
    }
    ...

The Kit property has the TransactionKit instance, which uses the example.db file as a database backend and writes log messages to the console. The Execute method executes the queries that the lambda expression of its parameter performs atomically (as a single transaction).

Note that calling the Insert(object) method with the Actor instance ignores its Id property, which is specified with the first parameter of the constructor of the Actor class, because it is qualified with the AutoIncrement attribute.

The log messages that the CreateTableAndInsertRows() method prints to the console are as follows:

DROP TABLE IF EXISTS Actors
CREATE TABLE Actors (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)
INSERT INTO Actors (name) VALUES ($name)
  ($name, Chloë Grace Moretz)
INSERT INTO Actors (name) VALUES ($name)
  ($name, Gary Carr)
INSERT INTO Actors (name) VALUES ($name)
  ($name, Jack Reynor)

The non-indented lines are actual SQL statements that were automatically generated and executed.

How to select a table and get rows

Then run the SelectAllRows() method as follows:

public sealed class Example
{
    ...
    public void SelectAllRows()
    {
        Kit.Execute(q =>
        {
            var all = q.SelectAll<Actor>();
            foreach (var i in all)
            {
                Console.WriteLine(i);
            }
        });
    }
    ...

The SelectAllRows() method outputs:

SELECT id, name FROM Actors
Actor { Id = 1, Name = Chloë Grace Moretz }
Actor { Id = 2, Name = Gary Carr }
Actor { Id = 3, Name = Jack Reynor }

The first line is the log message that the TransactionKit instance prints. The SelectAll<T>() method generates this statement.

The next three lines are the messages that the WriteLine(object) method outputs within the foreach block.

Inner join with two or more tables

Consider the following Titles table:

Titles

id name
1 Peripheral

And the following Casts table:

Casts

id titleId actorId role
1 1 1 Flynne Fisher
2 1 2 Wilf Netherton
3 1 3 Burton Fisher

The classes that correspond to these tables are:

[Table("Titles")]
public record class Title(
    [Column("id")][PrimaryKey][AutoIncrement] long Id,
    [Column("name")] string Name)
{
}

[Table("Casts")]
public record class Cast(
    [Column("id")][PrimaryKey][AutoIncrement] long Id,
    [Column("titleId")] long TitleId,
    [Column("actorId")] long ActorId,
    [Column("role")] string Role)
{
}

The following code creates the tables and inserts the rows:

public sealed class Example
{
    ...
    public void CreateTables()
    {
        Kit.Execute(q =>
        {
            q.NewTables(typeof(Title));
            q.NewTables(typeof(Actor));
            q.NewTables(typeof(Cast));
            var titleId = q.InsertAndGetRowId(new Title(0, "Peripheral"));
            var allCasts = new (string Name, string Role)[]
            {
                ("Chloë Grace Moretz", "Flynne Fisher"),
                ("Gary Carr", "Wilf Netherton"),
                ("Jack Reynor", "Burton Fisher"),
            };
            foreach (var (name, role) in allCasts)
            {
                var actorId = q.InsertAndGetRowId(new Actor(0, name));
                q.Insert(new Cast(0, titleId, actorId, role));
            }
        });
    }
    ...

The log messages that the CreateTables() method prints to the console are as follows:

DROP TABLE IF EXISTS Titles
CREATE TABLE Titles (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)
DROP TABLE IF EXISTS Actors
CREATE TABLE Actors (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)
DROP TABLE IF EXISTS Casts
CREATE TABLE Casts (id INTEGER PRIMARY KEY AUTOINCREMENT, titleId INTEGER, actorId INTEGER, role TEXT)
INSERT INTO Titles (name) VALUES ($name)
  ($name, Peripheral)
select last_insert_rowid()
INSERT INTO Actors (name) VALUES ($name)
  ($name, Chloë Grace Moretz)
select last_insert_rowid()
INSERT INTO Casts (titleId, actorId, role) VALUES ($titleId, $actorId, $role)
  ($titleId, 1)
  ($role, Flynne Fisher)
  ($actorId, 1)
INSERT INTO Actors (name) VALUES ($name)
  ($name, Gary Carr)
select last_insert_rowid()
INSERT INTO Casts (titleId, actorId, role) VALUES ($titleId, $actorId, $role)
  ($titleId, 1)
  ($role, Wilf Netherton)
  ($actorId, 2)
INSERT INTO Actors (name) VALUES ($name)
  ($name, Jack Reynor)
select last_insert_rowid()
INSERT INTO Casts (titleId, actorId, role) VALUES ($titleId, $actorId, $role)
  ($titleId, 1)
  ($role, Burton Fisher)
  ($actorId, 3)

Let's suppose that you would like to get a list of the names of the actors who performed in the specified title. To do this, use the APIs as follows:

public sealed class Example
{
    ...
    public void ListActorNames(string title)
    {
        Kit.Execute(q =>
        {
            var map = new Dictionary<string, object>
            {
                ["$name"] = title,
            };
            var all = q.SelectAllFrom<Actor>("a")
                .InnerJoin<Cast>("c", "a.id = c.actorId")
                .InnerJoin<Title>("t", "t.id = c.titleId")
                .Where("t.name = $name", map)
                .Execute();
            foreach (var i in all)
            {
                Console.WriteLine(i.Name);
            }
        });
    }
    ...

Calling ListActorNames("Peripheral"); results in the following output:

SELECT a.id, a.name FROM Actors a INNER JOIN Casts c ON a.id = c.actorId INNER JOIN Titles t ON t.id = c.titleId WHERE t.name = $name
  ($name, Peripheral)
Chloë Grace Moretz
Gary Carr
Jack Reynor

Get started

SqlBind.CSharp is available as the NuGet-logo NuGet package.

API Reference