C#でSQLiteを使う方法|接続・CRUD・Entity Frameworkまで初心者向けに徹底解説

はじめに

C#で小規模なデスクトップアプリ、業務ツール、学習用アプリ、ローカル保存機能を作るとき、データベースとしてよく選ばれるのがSQLiteです。SQLiteはサーバーを立てずに利用できる軽量なデータベースで、1つのファイルとしてデータを保存できるため、C#アプリに組み込みやすいのが大きな特徴です。

この記事では、C#でSQLiteを使う方法を初心者向けに解説します。SQLiteの基礎、開発環境の準備、データベース接続、テーブル作成、CRUD操作、トランザクション、非同期処理、Entity Framework Coreでの利用方法、よくあるエラーと対処法まで、実務で使える形で順番に説明します。

C#でSQLiteを扱う方法は大きく分けて2つあります。1つはMicrosoft.Data.Sqliteなどを使ってSQLを直接実行する方法、もう1つはEntity Framework Coreを使ってC#のクラスとしてデータを操作する方法です。この記事では両方を扱うため、まずは基本的なADO.NET方式で仕組みを理解し、その後にEF Coreを使った実装へ進む流れで学習できます。

1. C#でSQLiteを使う前に知っておきたい基礎知識

1-1. SQLiteとは?C#アプリで使われる理由

SQLiteは、サーバー不要で動作する軽量なリレーショナルデータベースです。一般的なデータベースであるSQL ServerやMySQLは、データベースサーバーを起動してアプリから接続する構成が多いですが、SQLiteはアプリケーションから直接データベースファイルを読み書きします。

SQLite公式では、SQLiteを「小さく、高速で、自己完結した、信頼性の高いSQLデータベースエンジン」と説明しています。スマートフォン、PC、さまざまなアプリに組み込まれている広く使われているデータベースです。SQLite

C#アプリでSQLiteが使われる理由は、導入が簡単だからです。データベースサーバーのインストールやユーザー管理が不要で、.db.sqliteといったファイルを作成するだけでデータを保存できます。そのため、Windows Forms、WPF、コンソールアプリ、個人開発のツール、ローカルキャッシュ、設定保存などに向いています。

1-2. C#とSQLiteの主な利用シーン

C#とSQLiteの組み合わせは、以下のような場面でよく使われます。

個人用のタスク管理アプリ、家計簿アプリ、在庫管理ツール、顧客管理ツール、学習用のサンプルアプリ、Windows FormsやWPFのローカルデータ保存、ASP.NET Coreの小規模アプリや開発環境用データベースなどです。

特に、アプリ単体で完結させたい場合にSQLiteは便利です。たとえば、社内の小さな業務ツールを配布するとき、SQL Serverを別途用意する必要があると運用が重くなります。一方、SQLiteであれば、アプリと一緒にデータベースファイルを配置するだけで動かせます。

ただし、SQLiteは万能ではありません。多数のユーザーが同時に書き込むWebサービスや、大規模な分析基盤、複雑な権限管理が必要なシステムでは、SQL Server、PostgreSQL、MySQLなどのサーバー型データベースの方が適しています。

1-3. SQLiteとSQL Server・MySQLの違い

SQLiteとSQL Server・MySQLの大きな違いは、サーバー型かファイル型かです。

SQLiteはファイル型データベースです。データベースの実体は1つのファイルで、C#アプリからそのファイルに直接アクセスします。サーバープロセスは不要です。

SQL ServerやMySQLはサーバー型データベースです。データベースサーバーが常駐し、アプリケーションはネットワークやローカル接続を通じてサーバーに問い合わせます。ユーザー管理、権限管理、同時接続、大規模データ処理などに強い一方、セットアップや運用はSQLiteより複雑です。

SQLiteは軽量で導入しやすく、ローカルアプリに向いています。一方で、複数ユーザーが同時に大量の書き込みを行う用途には注意が必要です。SQLiteは複数の読み取り接続を扱えますが、書き込みについては同時に1つの書き込みが基本になります。SQLite公式ドキュメントでも、1つのSQLiteデータベースに対して同時に存在できる書き込み処理は1つだけであると説明されています。SQLite

1-4. 初心者がC#でSQLiteを使うときの全体像

C#でSQLiteを使う流れは、次のように考えるとわかりやすいです。

まず、Visual StudioなどでC#プロジェクトを作成します。次に、NuGetでSQLite用のパッケージをインストールします。代表的なのはMicrosoft.Data.Sqliteです。次に、接続文字列を作成し、SqliteConnectionでデータベースに接続します。その後、SQL文を使ってテーブル作成、INSERT、SELECT、UPDATE、DELETEを実行します。

基本的な流れは次の通りです。

C#
using Microsoft.Data.Sqlite;

var connectionString = "Data Source=sample.db";

using var connection = new SqliteConnection(connectionString);
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SELECT sqlite_version();";

var version = command.ExecuteScalar();
Console.WriteLine($"SQLite version: {version}");

このコードでは、sample.dbというSQLiteデータベースファイルに接続し、SQLiteのバージョンを取得しています。ファイルが存在しない場合は、接続時に作成されます。

2. C#でSQLiteを使うための開発環境を準備する

2-1. 必要な開発環境と前提知識

C#でSQLiteを使うには、以下の環境があるとスムーズです。

Windows環境であればVisual Studioを使うのが一般的です。コンソールアプリ、Windows Forms、WPF、ASP.NET Coreなど、どの種類のC#プロジェクトでもSQLiteは利用できます。

前提知識としては、C#の基本文法、クラス、メソッド、using文、例外処理、SQLの基本的なSELECT・INSERT・UPDATE・DELETEを理解していると学習しやすくなります。

SQLに慣れていない場合でも、この記事のサンプルを順番に実行すれば基本的な操作は理解できます。

2-2. Visual StudioでC#プロジェクトを作成する

Visual StudioでC#プロジェクトを作成する手順は次の通りです。

Visual Studioを起動し、「新しいプロジェクトの作成」を選択します。学習目的であれば「コンソール アプリ」を選ぶとわかりやすいです。プロジェクト名を入力し、保存先を指定して作成します。

作成後、Program.csにSQLite接続やCRUD処理のコードを書いていきます。Windows FormsやWPFで使う場合も、最初はコンソールアプリでSQLiteの基本操作を確認してから画面アプリに組み込むと理解しやすくなります。

2-3. NuGetでSQLite関連パッケージをインストールする

C#からSQLiteを扱うには、NuGetパッケージを追加します。初心者におすすめなのはMicrosoft.Data.Sqliteです。

Visual Studioの場合は、プロジェクトを右クリックし、「NuGet パッケージの管理」を開きます。「参照」タブでMicrosoft.Data.Sqliteを検索し、インストールします。

.NET CLIを使う場合は、ターミナルで次のコマンドを実行します。

Bash
dotnet add package Microsoft.Data.Sqlite

Entity Framework CoreでSQLiteを使う場合は、次のパッケージを追加します。

Bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design

Microsoft.Data.SqliteはSQLite用の軽量なADO.NETプロバイダーです。Microsoftのドキュメントでも、EF CoreのSQLiteプロバイダーはMicrosoft.Data.Sqliteの上に構築されており、単体でも利用できると説明されています。Microsoft Learn

2-4. Microsoft.Data.SqliteとSystem.Data.SQLiteの違い

C#でSQLiteを検索すると、Microsoft.Data.SqliteSystem.Data.SQLiteの2種類を見かけることがあります。

Microsoft.Data.Sqliteは、Microsoftが提供する軽量なSQLite用ADO.NETプロバイダーです。Entity Framework Coreとの相性がよく、現在の.NETアプリではよく使われます。

System.Data.SQLiteは、SQLiteチームに関連する歴史の長いADO.NETプロバイダーです。既存の古いプロジェクトや.NET Frameworkアプリで使われていることがあります。

新規に.NETのC#アプリを作るなら、まずはMicrosoft.Data.Sqliteを選ぶとよいでしょう。EF Coreを使う場合もMicrosoft.EntityFrameworkCore.Sqliteを追加すれば、SQLiteを扱えます。既存システムがSystem.Data.SQLiteで作られている場合は、無理に置き換えず、既存コードとの互換性を優先します。

2-5. SQLiteデータベースファイルを作成する方法

SQLiteのデータベースファイルは、接続時に自動作成できます。たとえば、次のように接続文字列でData Source=app.dbを指定すると、app.dbが存在しない場合に作成されます。

C#
using Microsoft.Data.Sqlite;

using var connection = new SqliteConnection("Data Source=app.db");
connection.Open();

Console.WriteLine("SQLiteデータベースに接続しました。");

ただし、ファイルを作成できるかどうかは、アプリの実行場所や権限に依存します。たとえば、Program Files配下にアプリを配置している場合、書き込み権限がなくDBファイルを作成できないことがあります。

実務では、ユーザーごとのアプリデータフォルダや、アプリの設定フォルダにDBファイルを保存することが多いです。

C#
var folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var appFolder = Path.Combine(folder, "MySqliteApp");
Directory.CreateDirectory(appFolder);

var dbPath = Path.Combine(appFolder, "app.db");
var connectionString = $"Data Source={dbPath}";

3. C#からSQLiteに接続する方法

3-1. SQLiteの接続文字列の書き方

SQLiteの接続文字列では、主にData Sourceを指定します。Data Sourceにはデータベースファイルのパスを指定します。

C#
var connectionString = "Data Source=sample.db";

絶対パスを指定する場合は次のように書きます。

C#
var connectionString = @"Data Source=C:\Data\sample.db";

相対パスを指定する場合、実行時のカレントディレクトリを基準に解決されます。

C#
var connectionString = "Data Source=Data/sample.db";

Microsoft.Data.Sqliteの接続文字列は、ADO.NETで一般的な「キーワード=値」をセミコロンで区切る形式です。Microsoftのドキュメントでは、Data SourceModeCachePasswordなどのキーワードが説明されています。Microsoft Learn

3-2. SqliteConnectionでデータベースに接続する

C#からSQLiteに接続するには、SqliteConnectionを使います。

C#
using Microsoft.Data.Sqlite;

var connectionString = "Data Source=sample.db";

using var connection = new SqliteConnection(connectionString);
connection.Open();

Console.WriteLine("接続成功");

connection.Open()を呼び出すと、SQLiteデータベースへの接続が開かれます。データベースファイルが存在しない場合は、指定した場所に新しく作成されます。

接続できたら、SqliteCommandを使ってSQLを実行します。

C#
using var command = connection.CreateCommand();
command.CommandText = "SELECT datetime('now');";

var result = command.ExecuteScalar();
Console.WriteLine(result);

ExecuteScalar()は、1行1列の値を取得したいときに便利です。件数、ID、日付、単一の結果などを取得する場面で使います。

3-3. using文で接続を安全に閉じる方法

SQLiteに限らず、データベース接続は使い終わったら必ず閉じる必要があります。C#ではusing文やusing varを使うことで、処理が終わったときに自動的に破棄できます。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Users";

var count = command.ExecuteScalar();
Console.WriteLine(count);

usingを使わずに接続やリーダーを開いたままにすると、DBファイルがロックされたり、リソースが解放されなかったりする原因になります。

特にSQLiteはファイルベースのデータベースであるため、接続やトランザクションを長時間開いたままにしないことが重要です。

3-4. 接続エラーが起きる原因と対処法

C#からSQLiteに接続できない場合、よくある原因は次の通りです。

データベースファイルの保存先フォルダが存在しない、書き込み権限がない、接続文字列のパスが間違っている、必要なNuGetパッケージが入っていない、32bit・64bitの依存関係が合っていない、既存の接続やトランザクションがDBファイルをロックしている、などです。

特に初心者がつまずきやすいのは、相対パスです。Visual Studioで実行すると、相対パスはソースコードの場所ではなく、ビルドされた実行ファイルの場所を基準に解決されることがあります。

確認用に、次のようにフルパスを出力すると原因を特定しやすくなります。

C#
var dbPath = Path.GetFullPath("sample.db");
Console.WriteLine(dbPath);

3-5. 相対パス・絶対パスでDBファイルを指定する注意点

相対パスは手軽ですが、実行環境によって参照先が変わることがあります。開発中は問題なく動いても、配布後にDBファイルが見つからないことがあります。

安全に扱うなら、アプリ用の保存フォルダを明示的に作成し、絶対パスで指定する方法がおすすめです。

C#
var baseFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var appFolder = Path.Combine(baseFolder, "SampleApp");
Directory.CreateDirectory(appFolder);

var dbPath = Path.Combine(appFolder, "sample.db");
var connectionString = $"Data Source={dbPath}";

この方法なら、ユーザーごとのローカルアプリデータフォルダにDBを保存できます。Windowsアプリを配布する場合にも扱いやすくなります。

4. C#とSQLiteでテーブルを作成する

4-1. SQLでテーブルを作成する基本構文

SQLiteでテーブルを作成するには、CREATE TABLE文を使います。

SQL
CREATE TABLE Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Age INTEGER
);

このSQLでは、Usersというテーブルを作成しています。Idは主キー、Nameは必須項目、Emailは文字列、Ageは整数として保存します。

SQLiteでは、一般的なSQLと同じようにテーブル、カラム、主キー、制約を定義できます。ただし、データ型の扱いはSQL ServerやMySQLとは少し異なります。

4-2. C#からCREATE TABLEを実行する方法

C#からテーブルを作成するには、SqliteCommandCREATE TABLE文を実行します。

C#
using Microsoft.Data.Sqlite;

var connectionString = "Data Source=sample.db";

using var connection = new SqliteConnection(connectionString);
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Age INTEGER
);
";

command.ExecuteNonQuery();

Console.WriteLine("テーブルを作成しました。");

ExecuteNonQuery()は、結果セットを返さないSQLを実行するときに使います。CREATE TABLEINSERTUPDATEDELETEなどでよく使います。

4-3. 主キー・AUTOINCREMENT・NOT NULLの設定

PRIMARY KEYは、テーブル内の行を一意に識別するための制約です。C#アプリでは、各レコードを識別するIDとしてよく使います。

SQL
Id INTEGER PRIMARY KEY AUTOINCREMENT

SQLiteでは、INTEGER PRIMARY KEYのカラムは行IDとして扱われます。AUTOINCREMENTを付けると、過去に使われたIDの再利用を避ける動作になります。ただし、通常はINTEGER PRIMARY KEYだけでも自動的にIDが割り当てられるため、厳密にID再利用を避けたい場合にAUTOINCREMENTを使うと考えるとよいです。SQLite公式ドキュメントでも、AUTOINCREMENTは通常の行ID選択アルゴリズムを変更するキーワードとして説明されています。SQLite

NOT NULLは、NULLを許可しない制約です。たとえば、ユーザー名を必須にしたい場合は次のようにします。

SQL
Name TEXT NOT NULL

4-4. SQLiteで使える主なデータ型

SQLiteの主なストレージクラスは、NULLINTEGERREALTEXTBLOBです。SQLiteでは、カラムに型の「親和性」があり、TEXTNUMERICINTEGERREALBLOBのような型親和性に基づいて値が保存されます。SQLite

C#で扱うときは、だいたい次のように対応させるとわかりやすいです。

C#の型SQLiteの型の例用途
int, longINTEGERID、件数、年齢
doubleREAL小数、計算結果
stringTEXT名前、メール、説明文
byte[]BLOB画像、バイナリ
DateTimeTEXTまたはINTEGER日付、時刻
boolINTEGER0または1

SQLiteにはSQL Serverのような厳密なDATETIME型やBOOLEAN型があるわけではありません。そのため、日付はISO 8601形式の文字列として保存する、真偽値は0・1で保存する、といったルールをアプリ側で決めておくことが重要です。

4-5. テーブルが存在しない場合だけ作成する方法

アプリ起動時に毎回テーブル作成SQLを実行したい場合は、CREATE TABLE IF NOT EXISTSを使います。

SQL
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Age INTEGER
);

IF NOT EXISTSを付けると、すでに同名のテーブルが存在する場合はエラーになりません。C#アプリでは、初回起動時にテーブルを作成し、2回目以降は何もしないという処理を簡単に実装できます。

C#
static void InitializeDatabase()
{
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Age INTEGER
);
";

command.ExecuteNonQuery();
}

5. C#でSQLiteのCRUD操作を実装する

5-1. INSERTでデータを登録する方法

データを登録するにはINSERT文を使います。

C#
using Microsoft.Data.Sqlite;

using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO Users (Name, Email, Age)
VALUES ($name, $email, $age);
";

command.Parameters.AddWithValue("$name", "山田太郎");
command.Parameters.AddWithValue("$email", "yamada@example.com");
command.Parameters.AddWithValue("$age", 30);

command.ExecuteNonQuery();

Console.WriteLine("データを登録しました。");

ここでは、SQL文に直接値を埋め込まず、$name$email$ageというパラメータを使っています。C#でSQLiteを扱うときは、このようにパラメータ化クエリを使うのが基本です。

5-2. SELECTでデータを取得する方法

データを取得するにはSELECT文を使います。複数行を読み取る場合は、SqliteDataReaderを使います。

C#
using Microsoft.Data.Sqlite;

using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
SELECT Id, Name, Email, Age
FROM Users
ORDER BY Id;
";

using var reader = command.ExecuteReader();

while (reader.Read())
{
var id = reader.GetInt32(0);
var name = reader.GetString(1);
var email = reader.IsDBNull(2) ? "" : reader.GetString(2);
var age = reader.IsDBNull(3) ? 0 : reader.GetInt32(3);

Console.WriteLine($"{id}: {name}, {email}, {age}");
}

reader.Read()は、次の行が存在する間trueを返します。GetInt32()GetString()でカラムの値を取得できます。

NULLが入る可能性があるカラムは、reader.IsDBNull()で確認してから取得します。これを忘れると、NULL値を読み取ったときに例外が発生することがあります。

5-3. UPDATEでデータを更新する方法

既存データを更新するにはUPDATE文を使います。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
UPDATE Users
SET Email = $email,
Age = $age
WHERE Id = $id;
";

command.Parameters.AddWithValue("$email", "new-yamada@example.com");
command.Parameters.AddWithValue("$age", 31);
command.Parameters.AddWithValue("$id", 1);

var rows = command.ExecuteNonQuery();

Console.WriteLine($"{rows}件更新しました。");

ExecuteNonQuery()の戻り値は、影響を受けた行数です。WHERE句を書き忘れると、すべての行が更新されてしまうため注意してください。

5-4. DELETEでデータを削除する方法

データを削除するにはDELETE文を使います。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
DELETE FROM Users
WHERE Id = $id;
";

command.Parameters.AddWithValue("$id", 1);

var rows = command.ExecuteNonQuery();

Console.WriteLine($"{rows}件削除しました。");

DELETEでもWHERE句の書き忘れに注意が必要です。WHERE句がないとテーブル内の全データが削除されます。

実務では、物理削除ではなくIsDeletedのようなフラグを用意して論理削除にすることもあります。

SQL
ALTER TABLE Users ADD COLUMN IsDeleted INTEGER NOT NULL DEFAULT 0;

論理削除の場合は、削除時にIsDeleted = 1へ更新します。

SQL
UPDATE Users SET IsDeleted = 1 WHERE Id = $id;

5-5. SqliteCommandとSqliteDataReaderの使い方

SqliteCommandは、SQL文を実行するためのクラスです。INSERT、SELECT、UPDATE、DELETE、CREATE TABLEなど、SQLiteに対して命令を送るときに使います。

代表的な実行メソッドは次の通りです。

メソッド用途
ExecuteNonQuery()INSERT、UPDATE、DELETE、CREATE TABLEなど
ExecuteScalar()1つの値を取得する
ExecuteReader()複数行の結果を読み取る

SqliteDataReaderは、SELECTの結果を1行ずつ読み取るためのクラスです。大量のデータをすべてメモリに読み込むのではなく、順番に読み取るため効率的です。

C#
using var command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Users;";

var count = Convert.ToInt32(command.ExecuteScalar());
Console.WriteLine($"ユーザー数: {count}");

1件だけ取得したい場合は、ExecuteScalar()が簡単です。複数行を扱う場合は、ExecuteReader()を使います。

5-6. パラメータ化クエリでSQLインジェクションを防ぐ方法

C#でSQLiteを使うとき、SQL文字列にユーザー入力を直接連結してはいけません。

悪い例は次のようなコードです。

C#
var name = "山田";
command.CommandText = $"SELECT * FROM Users WHERE Name = '{name}'";

この書き方は、SQLインジェクションの原因になります。ユーザー入力に悪意あるSQLが含まれていた場合、意図しないSQLが実行される可能性があります。

安全な書き方は、パラメータ化クエリです。

C#
using var command = connection.CreateCommand();
command.CommandText =
@"
SELECT Id, Name, Email, Age
FROM Users
WHERE Name = $name;
";

command.Parameters.AddWithValue("$name", name);

INSERT、UPDATE、DELETE、SELECTのすべてで、外部入力を使う場合は必ずパラメータを使いましょう。検索条件、ID、日付、文字列など、値は基本的にパラメータで渡します。

6. C#とSQLiteの実践的な使い方

6-1. 複数レコードを効率よく登録する方法

複数のレコードを登録するとき、1件ごとに接続を開閉すると効率が悪くなります。接続はまとめて開き、同じコマンドを使い回すと処理が速くなります。

C#
var users = new[]
{
new { Name = "佐藤", Email = "sato@example.com", Age = 25 },
new { Name = "鈴木", Email = "suzuki@example.com", Age = 28 },
new { Name = "田中", Email = "tanaka@example.com", Age = 32 }
};

using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO Users (Name, Email, Age)
VALUES ($name, $email, $age);
";

var nameParam = command.CreateParameter();
nameParam.ParameterName = "$name";
command.Parameters.Add(nameParam);

var emailParam = command.CreateParameter();
emailParam.ParameterName = "$email";
command.Parameters.Add(emailParam);

var ageParam = command.CreateParameter();
ageParam.ParameterName = "$age";
command.Parameters.Add(ageParam);

foreach (var user in users)
{
nameParam.Value = user.Name;
emailParam.Value = user.Email;
ageParam.Value = user.Age;

command.ExecuteNonQuery();
}

このコードでは、コマンドとパラメータを再利用しています。少量のデータであればAddWithValue()でも問題ありませんが、大量データではパラメータを作成して値だけ差し替える方が効率的です。

6-2. トランザクションを使って処理を安全に実行する

複数の処理をまとめて成功・失敗させたい場合は、トランザクションを使います。たとえば、100件のデータ登録中に途中でエラーが起きた場合、中途半端に50件だけ登録されるのを防げます。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var transaction = connection.BeginTransaction();

try
{
using var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText =
@"
INSERT INTO Users (Name, Email, Age)
VALUES ($name, $email, $age);
";

var nameParam = command.CreateParameter();
nameParam.ParameterName = "$name";
command.Parameters.Add(nameParam);

var emailParam = command.CreateParameter();
emailParam.ParameterName = "$email";
command.Parameters.Add(emailParam);

var ageParam = command.CreateParameter();
ageParam.ParameterName = "$age";
command.Parameters.Add(ageParam);

for (int i = 1; i <= 100; i++)
{
nameParam.Value = $"User{i}";
emailParam.Value = $"user{i}@example.com";
ageParam.Value = 20 + (i % 10);

command.ExecuteNonQuery();
}

transaction.Commit();
Console.WriteLine("登録が完了しました。");
}
catch
{
transaction.Rollback();
Console.WriteLine("エラーが発生したためロールバックしました。");
throw;
}

大量のINSERTでは、トランザクションを使うことでパフォーマンスも改善しやすくなります。SQLiteはファイルにデータを書き込むため、1件ずつ確定するより、まとめて確定した方が効率的です。

6-3. 非同期処理でSQLiteにアクセスする方法

C#では、OpenAsync()ExecuteNonQueryAsync()ExecuteReaderAsync()などを使って非同期処理を書けます。

C#
using Microsoft.Data.Sqlite;

static async Task AddUserAsync(string name, string email, int age)
{
using var connection = new SqliteConnection("Data Source=sample.db");
await connection.OpenAsync();

using var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO Users (Name, Email, Age)
VALUES ($name, $email, $age);
";

command.Parameters.AddWithValue("$name", name);
command.Parameters.AddWithValue("$email", email);
command.Parameters.AddWithValue("$age", age);

await command.ExecuteNonQueryAsync();
}

Windows FormsやWPFでは、UIスレッドで重いデータベース処理を実行すると画面が固まることがあります。非同期処理を使うことで、UIの応答性を保ちやすくなります。

ただし、非同期にしたからといってSQLiteの同時書き込み制限がなくなるわけではありません。複数の処理が同時に書き込もうとするとロックが発生する可能性があります。

6-4. 検索条件を指定してデータを絞り込む方法

検索条件を指定するには、WHERE句を使います。たとえば、年齢が30歳以上のユーザーを取得する場合は次のようにします。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText =
@"
SELECT Id, Name, Email, Age
FROM Users
WHERE Age >= $age
ORDER BY Age DESC;
";

command.Parameters.AddWithValue("$age", 30);

using var reader = command.ExecuteReader();

while (reader.Read())
{
Console.WriteLine($"{reader.GetInt32(0)}: {reader.GetString(1)}");
}

文字列検索ではLIKEを使います。

C#
command.CommandText =
@"
SELECT Id, Name, Email, Age
FROM Users
WHERE Name LIKE $keyword;
";

command.Parameters.AddWithValue("$keyword", "%山田%");

ユーザーが入力した検索キーワードを使う場合も、文字列連結ではなくパラメータを使います。

6-5. 日付・数値・文字列を扱うときの注意点

SQLiteで日付を扱う場合、保存形式を決めておくことが重要です。おすすめは、ISO 8601形式の文字列として保存する方法です。

C#
var now = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

テーブル定義では次のようにTEXTとして保存します。

SQL
CreatedAt TEXT NOT NULL

登録時は次のようにします。

C#
command.CommandText =
@"
INSERT INTO Users (Name, Email, Age, CreatedAt)
VALUES ($name, $email, $age, $createdAt);
";

command.Parameters.AddWithValue("$createdAt", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"));

数値については、整数ならINTEGER、小数ならREALを使います。金額のように誤差を避けたい値は、円単位の整数として保存する、またはアプリ側で扱い方を統一するなどの設計が必要です。

Entity Framework CoreでSQLiteを使う場合、SQLiteがネイティブに対応しない型にも注意が必要です。MicrosoftのEF Core SQLiteプロバイダーのドキュメントでは、DateTimeOffsetdecimalTimeSpanulongなどについて、一部の比較や並べ替えがクライアント評価になる場合があると説明されています。Microsoft Learn

6-6. DBファイルをアプリ配布時に含める方法

アプリ配布時に初期データ入りのSQLiteファイルを含めたい場合は、プロジェクトに.dbファイルを追加し、ビルド時に出力フォルダへコピーする設定を行います。

Visual Studioでは、DBファイルをプロジェクトに追加し、プロパティで「出力ディレクトリにコピー」を「新しい場合はコピー」または「常にコピー」に設定します。

ただし、出力フォルダにコピーしたDBファイルをそのまま更新する設計には注意が必要です。アプリのインストール先によっては書き込み権限がない場合があります。

実務では、初回起動時に同梱したDBファイルをユーザーのアプリデータフォルダへコピーし、そのコピーを更新する方法がよく使われます。

C#
var localFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var appFolder = Path.Combine(localFolder, "SampleApp");
Directory.CreateDirectory(appFolder);

var dbPath = Path.Combine(appFolder, "sample.db");

if (!File.Exists(dbPath))
{
var sourcePath = Path.Combine(AppContext.BaseDirectory, "sample.db");
File.Copy(sourcePath, dbPath);
}

この方法なら、アプリ本体のフォルダを汚さず、ユーザーごとのデータとしてSQLiteファイルを管理できます。

7. Entity Framework CoreでSQLiteを使う方法

7-1. EF CoreでSQLiteを使うメリット

Entity Framework Core、通称EF Coreは、C#のクラスを使ってデータベースを操作できるORMです。SQLを直接書かなくても、LINQやC#のオブジェクト操作でデータの登録、取得、更新、削除ができます。

EF Coreを使うメリットは、コードの見通しがよくなること、C#の型としてデータを扱えること、マイグレーションでテーブル変更を管理できることです。

MicrosoftのEntity Frameworkのドキュメントでは、EFは.NET向けのオブジェクトリレーショナルマッパーであり、LINQクエリ、変更追跡、更新、スキーママイグレーションをサポートすると説明されています。Microsoft Learn

小規模なアプリでSQLを直接書きたい場合はADO.NET方式で十分です。一方、テーブル数が増えるアプリや、モデル中心で開発したいアプリではEF Coreが便利です。

7-2. 必要なNuGetパッケージをインストールする

EF CoreでSQLiteを使うには、次のパッケージをインストールします。

Bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design

マイグレーションコマンドを使う場合は、dotnet-efツールも必要です。

Bash
dotnet tool install --global dotnet-ef

すでにインストール済みの場合は、必要に応じて更新します。

Bash
dotnet tool update --global dotnet-ef

Microsoft.EntityFrameworkCore.Sqliteは、EF CoreでSQLiteを利用するためのプロバイダーです。Microsoftのドキュメントでも、このプロバイダーによりEntity Framework CoreでSQLiteを使用できると説明されています。Microsoft Learn

7-3. DbContextとエンティティクラスを作成する

EF Coreでは、テーブルに対応するクラスをエンティティと呼びます。まず、ユーザーを表すUserクラスを作成します。

C#
public class User
{
public int Id { get; set; }

public string Name { get; set; } = string.Empty;

public string? Email { get; set; }

public int Age { get; set; }
}

次に、データベースとのやり取りを管理するDbContextを作成します。

C#
using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
public DbSet<User> Users => Set<User>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=app.db");
}
}

DbSet<User>は、Usersテーブルに対応します。UseSqlite()でSQLiteの接続文字列を指定します。

7-4. 接続文字列を設定する

簡単なコンソールアプリでは、OnConfiguring()に直接接続文字列を書いても問題ありません。

C#
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=app.db");
}

ただし、実務では接続文字列を設定ファイルに分離することが多いです。ASP.NET Coreではappsettings.jsonに書きます。

JSON
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=app.db"
}
}

そして、Program.csで読み込みます。

C#
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

コンソールアプリやWPFアプリでも、設定ファイルや環境変数から接続文字列を読み込む設計にすると、環境ごとの差し替えがしやすくなります。

7-5. マイグレーションでテーブルを作成する

EF Coreでは、マイグレーションを使ってデータベーススキーマを管理できます。まず、マイグレーションを作成します。

Bash
dotnet ef migrations add InitialCreate

次に、データベースへ反映します。

Bash
dotnet ef database update

これにより、UserクラスやDbContextの定義に基づいてSQLiteデータベースにテーブルが作成されます。

マイグレーションを使うと、テーブル定義の変更履歴をコードとして管理できます。あとからカラムを追加したり、制約を変更したりする場合にも便利です。

ただし、SQLiteは一部のALTER TABLE操作に制限があります。そのため、EF Coreのマイグレーションで複雑なスキーマ変更を行う場合は、SQLiteプロバイダーの制限を確認しておくと安心です。Microsoftのドキュメントでも、SQLite EF Coreプロバイダーにはデータ型やスキーマ操作に関する制限があることが説明されています。Microsoft Learn

7-6. EF CoreでCRUD操作を実装する

EF Coreでデータを登録するには、DbContextを作成し、Add()してSaveChanges()を呼びます。

C#
using var db = new AppDbContext();

var user = new User
{
Name = "山田太郎",
Email = "yamada@example.com",
Age = 30
};

db.Users.Add(user);
db.SaveChanges();

データを取得するには、LINQを使います。

C#
using var db = new AppDbContext();

var users = db.Users
.Where(u => u.Age >= 20)
.OrderBy(u => u.Id)
.ToList();

foreach (var user in users)
{
Console.WriteLine($"{user.Id}: {user.Name}");
}

データを更新するには、対象データを取得してプロパティを変更し、SaveChanges()を呼びます。

C#
using var db = new AppDbContext();

var user = db.Users.FirstOrDefault(u => u.Id == 1);

if (user != null)
{
user.Email = "new-yamada@example.com";
user.Age = 31;

db.SaveChanges();
}

データを削除するには、Remove()を使います。

C#
using var db = new AppDbContext();

var user = db.Users.FirstOrDefault(u => u.Id == 1);

if (user != null)
{
db.Users.Remove(user);
db.SaveChanges();
}

非同期で処理する場合は、ToListAsync()FirstOrDefaultAsync()SaveChangesAsync()を使います。

C#
using Microsoft.EntityFrameworkCore;

using var db = new AppDbContext();

var users = await db.Users
.Where(u => u.Age >= 20)
.ToListAsync();

7-7. ADO.NETとEntity Framework Coreの使い分け

C#でSQLiteを使う場合、ADO.NET方式とEF Core方式のどちらを選ぶべきか迷うことがあります。

SQLを直接書きたい、軽量に実装したい、処理内容を細かく制御したい、学習目的でSQLiteの仕組みを理解したい場合は、Microsoft.Data.Sqliteを使ったADO.NET方式が向いています。

一方、テーブル数が多い、C#のクラス中心で開発したい、LINQでデータを操作したい、マイグレーションでスキーマを管理したい場合は、EF Coreが向いています。

初心者の場合は、まずADO.NET方式で接続、CREATE TABLE、INSERT、SELECT、UPDATE、DELETEを理解し、その後にEF Coreを学ぶのがおすすめです。SQLの動きがわかっていると、EF Coreの裏側で何が起きているかも理解しやすくなります。

8. C#でSQLiteを使うときによくあるエラーと解決策

8-1. database is lockedが発生する原因と対処法

database is lockedは、SQLiteでよくあるエラーです。主な原因は、別の接続やトランザクションがデータベースファイルをロックしていることです。

SQLiteはファイルベースのデータベースであり、書き込みにはロックが関係します。SQLiteのロックに関する公式ドキュメントでは、書き込みにはEXCLUSIVEロックが必要で、同時に許可されるEXCLUSIVEロックは1つだけであると説明されています。SQLite

対処法としては、接続、コマンド、リーダーをusingで確実に破棄する、トランザクションを長時間開いたままにしない、同時書き込みを避ける、短時間で処理を終える、必要に応じてリトライ処理を入れる、WALモードを検討する、といった方法があります。

WALモードでは読み取りと書き込みの並行性が改善される場合がありますが、SQLite公式ドキュメントでも、WALファイルは1つであるため書き込みは同時に1つであると説明されています。SQLite

WALモードを有効にする例は次の通りです。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "PRAGMA journal_mode=WAL;";
command.ExecuteNonQuery();

ただし、WALモードにすればすべてのロック問題が解消するわけではありません。基本は、接続やトランザクションを短く保つことです。

8-2. no such tableが出る原因と確認ポイント

no such tableは、指定したテーブルが存在しない場合に発生します。

よくある原因は、テーブル作成処理を実行していない、接続しているDBファイルが想定と違う、相対パスの解決先が違う、マイグレーションを実行していない、テーブル名のスペルが間違っている、などです。

まず、実際に接続しているDBファイルのパスを確認しましょう。

C#
Console.WriteLine(Path.GetFullPath("sample.db"));

次に、CREATE TABLE IF NOT EXISTSが実行されているか確認します。

C#
using var command = connection.CreateCommand();
command.CommandText =
@"
CREATE TABLE IF NOT EXISTS Users (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Email TEXT,
Age INTEGER
);
";
command.ExecuteNonQuery();

EF Coreを使っている場合は、次のコマンドでマイグレーションをDBへ反映します。

Bash
dotnet ef database update

8-3. DBファイルのパスが見つからない場合の対処法

SQLiteのDBファイルが見つからない場合、多くはパス指定が原因です。

相対パスは実行ファイルの場所を基準に解決されることがあります。そのため、開発中と配布後で参照先が変わる可能性があります。

確実にするには、絶対パスを使います。

C#
var dbPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"SampleApp",
"sample.db"
);

Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);

var connectionString = $"Data Source={dbPath}";

また、フォルダが存在しない状態でDBファイルを作成しようとすると失敗することがあります。Directory.CreateDirectory()で先にフォルダを作成しておくと安全です。

8-4. SQLiteの型変換で起きやすいエラー

SQLiteは型の扱いが柔軟です。そのため、C#側で期待する型と実際の保存形式が合わないと、読み取り時にエラーが起きることがあります。

たとえば、AgeINTEGERとして扱いたいのに文字列が入っていると、GetInt32()で失敗する可能性があります。NULLが入る可能性があるカラムに対してGetString()GetInt32()を直接呼ぶのも危険です。

安全に読み取るには、IsDBNull()を確認します。

C#
var age = reader.IsDBNull(3) ? null : reader.GetInt32(3);

日付についても、保存形式を統一しましょう。文字列として保存するならISO 8601形式、数値として保存するならUnix時間など、アプリ全体でルールを決めておきます。

C#
var createdAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

8-5. NuGetパッケージの違いによるエラー

SQLite関連のC#パッケージには、Microsoft.Data.SqliteSystem.Data.SQLiteMicrosoft.EntityFrameworkCore.Sqliteなどがあります。これらを混在させると、名前空間やクラス名の違いで混乱することがあります。

Microsoft.Data.Sqliteを使う場合は、名前空間は次のようになります。

C#
using Microsoft.Data.Sqlite;

EF CoreでSQLiteを使う場合は、次の名前空間を使います。

C#
using Microsoft.EntityFrameworkCore;

System.Data.SQLiteを使った記事を参考にしていると、SQLiteConnectionのようにクラス名が異なることがあります。一方、Microsoft.Data.SqliteではSqliteConnectionです。

コードをコピーするときは、どのパッケージを前提にしたサンプルかを確認しましょう。この記事では、基本的にMicrosoft.Data.SqliteMicrosoft.EntityFrameworkCore.Sqliteを使っています。

9. C#とSQLiteを使う際の注意点とベストプラクティス

9-1. SQLはパラメータ化して安全に実行する

C#でSQLiteを使うときは、SQLに値を直接連結しないことが重要です。

悪い例は次の通りです。

C#
command.CommandText = $"SELECT * FROM Users WHERE Name = '{name}'";

安全な例は次の通りです。

C#
command.CommandText = "SELECT * FROM Users WHERE Name = $name";
command.Parameters.AddWithValue("$name", name);

パラメータ化クエリを使うことで、SQLインジェクションを防ぎやすくなります。検索フォーム、ログイン画面、入力フォームなど、ユーザー入力を使うSQLでは必ずパラメータを使いましょう。

9-2. 接続・コマンド・リーダーは適切に破棄する

SQLiteでは、接続やリーダーを開いたままにするとロックの原因になります。SqliteConnectionSqliteCommandSqliteDataReaderusingで破棄しましょう。

C#
using var connection = new SqliteConnection("Data Source=sample.db");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Users";

using var reader = command.ExecuteReader();

while (reader.Read())
{
Console.WriteLine(reader.GetString(1));
}

特に、SqliteDataReaderを閉じずに別の書き込み処理を行うと、ロックや例外の原因になることがあります。

9-3. 大量データ処理ではトランザクションを使う

大量のINSERT、UPDATE、DELETEを行う場合は、トランザクションを使いましょう。1件ずつコミットするより、まとめてコミットした方が処理が速くなり、安全性も高まります。

C#
using var transaction = connection.BeginTransaction();

try
{
// 複数のINSERTやUPDATEを実行
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}

トランザクションを使うことで、途中でエラーが起きた場合に変更を取り消せます。データの整合性を守るためにも、関連する複数処理は1つのトランザクションにまとめるのがおすすめです。

9-4. 複数ユーザー・同時書き込みに注意する

SQLiteはローカルアプリや小規模アプリに向いていますが、多数のユーザーが同時に書き込むシステムには注意が必要です。

読み取り中心であれば問題になりにくいですが、複数のプロセスやユーザーが同時に書き込みを行うと、database is lockedが発生しやすくなります。

また、ネットワーク共有フォルダ上のSQLiteファイルを複数端末から同時に使う設計は避けた方が安全です。SQLite公式FAQでも、NFS上のSQLiteデータベースファイルについて、複数プロセスが同時にアクセスする可能性がある場合は注意が必要であると説明されています。SQLite

複数ユーザーで同時利用する業務システムを作るなら、SQL Server、PostgreSQL、MySQLなどのサーバー型データベースを検討しましょう。

9-5. SQLiteが向いているケース・向いていないケース

SQLiteが向いているのは、ローカルアプリ、個人用ツール、小規模な業務アプリ、プロトタイプ、テスト用データベース、モバイルアプリ、組み込みアプリ、オフライン対応アプリなどです。

一方、SQLiteが向いていないのは、大量の同時書き込みがあるシステム、多数ユーザーが同時接続するWebサービス、細かい権限管理が必要なシステム、大規模な分散システム、ネットワーク越しにDBを共有するシステムなどです。

C#でSQLiteを使うときは、「アプリ内に軽量なデータ保存機能を持たせたい」「サーバーを用意せずに簡単に使いたい」という目的に合っているかを確認しましょう。

10. C# SQLiteに関するよくある質問

10-1. C#でSQLiteを使うならどのパッケージがおすすめ?

新規の.NETアプリであれば、まずはMicrosoft.Data.Sqliteがおすすめです。軽量で使いやすく、EF CoreのSQLiteプロバイダーの土台にもなっています。Microsoftのドキュメントでも、Microsoft.Data.SqliteはSQLite用の軽量ADO.NETプロバイダーとして説明されています。Microsoft Learn

Entity Framework Coreを使いたい場合は、Microsoft.EntityFrameworkCore.Sqliteを使います。SQLを直接書くならMicrosoft.Data.Sqlite、C#のクラスとLINQ中心で扱うならMicrosoft.EntityFrameworkCore.Sqliteと考えるとわかりやすいです。

既存プロジェクトがSystem.Data.SQLiteを使っている場合は、互換性を考えてそのまま使う選択もあります。

10-2. SQLiteのDBファイルはどこに保存すべき?

開発中の簡単なサンプルであれば、プロジェクトの実行フォルダに保存しても構いません。

ただし、実務や配布アプリでは、ユーザーごとのアプリデータフォルダに保存するのがおすすめです。

C#
var folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var appFolder = Path.Combine(folder, "MyApp");
Directory.CreateDirectory(appFolder);

var dbPath = Path.Combine(appFolder, "app.db");

この方法なら、アプリのインストール先に書き込み権限がなくても、ユーザーのデータとしてDBファイルを保存できます。

10-3. Windows FormsやWPFでもSQLiteは使える?

Windows FormsやWPFでもSQLiteは使えます。C#のコンソールアプリと同じように、Microsoft.Data.Sqliteをインストールし、SqliteConnectionで接続できます。

ただし、画面アプリではUIスレッドをブロックしないように注意が必要です。重い検索処理や大量登録処理は、非同期処理にするか、バックグラウンドで実行する設計にしましょう。

また、ボタンクリックのたびに接続を開き、処理が終わったらすぐ閉じるようにすると、ロック問題を避けやすくなります。

10-4. ASP.NET CoreでSQLiteを使ってもよい?

ASP.NET CoreでもSQLiteは使えます。開発環境、学習用、小規模なアプリ、プロトタイプであれば便利です。

EF Coreを使う場合は、Microsoft.EntityFrameworkCore.Sqliteを追加し、appsettings.jsonに接続文字列を書いてAddDbContext()で設定します。

C#
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

ただし、本番環境で多数ユーザーが同時に利用するWebアプリでは注意が必要です。読み取り中心の小規模アプリであれば使える場合もありますが、書き込みが多いサービスではSQL Server、PostgreSQL、MySQLなどを検討した方が安全です。

10-5. SQLiteからSQL Serverへ移行できる?

SQLiteからSQL Serverへ移行することは可能です。ただし、データ型、SQL文、制約、日付の扱い、ID採番、マイグレーションの内容などに違いがあるため、完全にそのまま移行できるとは限りません。

ADO.NETでSQLを直接書いている場合は、接続クラスやSQL文をSQL Server向けに修正する必要があります。

EF Coreを使っている場合は、プロバイダーをSQL Serverに変更することで移行しやすくなる場合があります。ただし、SQLite固有の制限やSQL Server固有の機能を使っている場合は調整が必要です。

将来的にSQL Serverへ移行する可能性があるなら、データアクセス層を分離しておく、SQLをアプリ全体に散らばらせない、EF Coreを使う、SQLite固有のSQLに依存しすぎない、といった設計にしておくと移行しやすくなります。

まとめ

C#でSQLiteを使うと、サーバー不要で手軽にデータ保存機能を実装できます。SQLiteは1つのファイルとして扱えるため、Windows Forms、WPF、コンソールアプリ、小規模なASP.NET Coreアプリ、学習用アプリなどに向いています。

基本的な流れは、NuGetでMicrosoft.Data.Sqliteを追加し、SqliteConnectionで接続し、SqliteCommandでSQLを実行するだけです。テーブル作成、INSERT、SELECT、UPDATE、DELETEといったCRUD操作を理解すれば、C#アプリにローカルデータベース機能を組み込めます。

安全に使うためには、SQLをパラメータ化する、接続やリーダーをusingで破棄する、大量データ処理ではトランザクションを使う、DBファイルの保存場所に注意する、同時書き込みを避けるといった基本が重要です。

より大きなアプリや、C#のクラス中心でデータを扱いたい場合は、Entity Framework CoreとSQLiteを組み合わせる方法もあります。EF Coreを使えば、エンティティクラス、DbContext、LINQ、マイグレーションを使って、より保守しやすいデータアクセス層を作れます。

初心者はまずMicrosoft.Data.SqliteでSQLを直接実行する方法を学び、その後にEntity Framework Coreへ進むと、C#とSQLiteの両方をしっかり理解できます。C#で軽量なデータベース機能を作りたいなら、SQLiteは非常に扱いやすい選択肢です。