作成するDBの設計図
ひとまずSampleということで、DriverID、DriverName、validと運送業のドライバの方々の情報を格納するDBを作成したいと思います。
データベースはSqlite3を使用します。
Driver Table
-- | DriverID | DriverName | valid |
---|---|---|---|
Type | INT | TEXT | BOOL |
PK | ○ | -- | -- |
UQ | ○ | -- | -- |
DEFAULT | -- | -- | false |
環境
OS : Ubuntu 24.04.3 LTS
IDE : VSCode 1.103.2
dotnet : 8.0.119
DB : Sqlite3 3.45.1
migrationをするための事前リサーチ
今回migrationすなわちC#からDBへのコードベースの簡易アクセスを行うツールとして、EF Coreとよばれるフレームワークを使用します[2]。
おおよそのその使い方は以下のようなイメージです。
- 作成するテーブル名を持ったクラスを作成
- DbContextを継承したクラスを作成(DbSetでテーブルクラスの定義)
- DBの設定
また、その流れは以下の図のようになるはずです。
実装
まずはプロジェクトにPackageをインストールします。
$ dotnet add package Microsoft.EntityFrameworkCore.Sqlite
...
$ dotnet list package
...
> Microsoft.EntityFrameworkCore.Sqlite 9.0.8 9.0.8
> Microsoft.EntityFrameworkCore.SqlServer 9.0.8 9.0.8
> Microsoft.EntityFrameworkCore.Tools 9.0.8 9.0.8
...
ディレクトリTree
.
├── Migartion
│ ├── DbContexts.cs
│ └── Driver.cs
├── Migrations
├── Program.cs
...
Entityの設定
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
namespace SampleAPI.Entities
{
public class Driver
{
[Key]
[Required]
public int DriverID { get; set; }
[MaxLength(40)]
[Required]
public required string DriverName { get; set; }
[Required]
public bool valid { get; set; } = false;
}
}
以上私が今回作ろうとしているテーブルのEntityです。
「Primary Keyの制約があるくせしてRequiredとは何事か?」みたいなツッコミはあると思いますが、目をつむっていただきたいです。
また、
public bool valid { get; set; } = false;
で=false;としてますが、これはDEFAULT制約を課すことができます。
後述しますが、これではDefault制約を課すことができなかったため別のアプローチが必要です。
また、基本的にはMicrosoft.EntityFrameworkCoreで用いる変数にはプロパティを使用します。
特にセッターの制限などは今回は必要ないため、自動実装プロパティ(get;set;)で良いと思います。
DBContextの設定
先ほど作成したエンティティを用いてContextを作成します。
当方、ContextはデータベースCRUDなどに必要な情報を定義するものだと把握しておりますが、実際のところどうかはわかりません。
using System;
using Microsoft.EntityFrameworkCore;
using SampleAPI.Entities;
namespace SampleAPI.DbContexts
{
public class DriverDbContext : DbContext
{
public DriverDbContext(DbContextOptions<DriverDbContext> options) : base(options) { }
public DbSet<Driver> Driver{ get; set; }
}
}
DBContextはMicrosoft.EntityFrameworkCore.DbContextを継承することで作成します。
public DbSet<Driver> Driver{ get; set; }
まず、以上のようにエンティティDriverをDriverプロパティとしてDriverDbContextクラスに定義しておきます。
public DriverDbContext(DbContextOptions<DriverDbContext> options) : base(options){}
そうして基底クラスのコンストラクタにoptionsインスタンスを投げることでDbContextを定義できました。
DBサービスの設定
上記で作成したDbContextを用いてMigrationできるようにProgram.csで記述していきます。
using Microsoft.EntityFrameworkCore;
using SampleAPI.DbContexts;
...
builder.Services.AddDbContext<DriverDbContext>(options =>
options.UseSqlite("Data Source=driver.db"));
...
Migrationの実行
$ dotnet ef migrations add InitialCreate
> Build started...
> Build succeeded.
> Done. To undo this action, use 'ef migrations remove'
$ dotnet ef database update
Project.csが存在するDirectoryにおいて上記を実行してください。
以下のようにdbが作成されているのが見て取れます。
$ find *db
> driver.db
また、正しくDriverテーブルが作成されているのがわかります。
sqlite> .mode line
sqlite> select * from sqlite_master;
...
type = table
name = Driver
tbl_name = Driver
rootpage = 5
sql = CREATE TABLE "Driver" (
"DriverID" INTEGER NOT NULL CONSTRAINT "PK_Driver" PRIMARY KEY AUTOINCREMENT,
"DriverName" TEXT NOT NULL,
"valid" INTEGER NOT NULL
)
...
正しくないことに今気が付きました。
validのdefault制約はどこに行ったのでしょうかね。
Default制約の見直し
DbContent.csを参照しても=false;が記述してあるのを確認できました。
即ちDefault制約の課し方が恐らく間違っているのでしょう。
どうやらContextにおいてOnModelCreatingというものを使用すればDefault制約を付与できる(正しくは制約を書き換える)らしいです[3][4]。
//DbContexts.cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Driver>()
.Property(d => d.Valid)
.HasDefaultValue(false);
}
そうして再度Migrationを行うと
type = table
name = Driver
tbl_name = Driver
rootpage = 7
sql = CREATE TABLE "Driver" (
"DriverID" INTEGER NOT NULL CONSTRAINT "PK_Driver" PRIMARY KEY AUTOINCREMENT,
"DriverName" TEXT NOT NULL,
"valid" INTEGER NOT NULL DEFAULT 0
)
正しくテーブルが登録できていることがわかりました。
おわりに
そういえばSqliteにはBool型なかったですね(๑><๑)
Sqlite←字面が完全にジュースのSprite
参考資料
[1]
[2]
[3] DbContext.OnModelCreating(ModelBuilder) メソッド
[4] 生成された値