砂漠の旅人(たびと)

UNIX / MS-DOS 時代から電脳砂漠を旅しています

【後編】.NET 6 から SQLite、PostgreSQL、MySQL、SQL Server、Oracle にEntity Framework Core で接続する

こんにちは、たびとです。

前回は、SQLite, PostgreSQL, MySQL を使った Entity Framework Core による簡単な C#ソースコードを使って、 実行後に、どんなDDLが生成されるのかを確認しました。

sabakunotabito.hatenablog.com

今回は、SQL Server および Oracle を確認していきます。

この記事の対象者

  • .NET 6 に興味のある方、これから使う予定のある方
  • 各データベースと Entity Framework Core の違いに興味のある方
  • SQL ServerOracle を docker で構築してみたい方

SQL Server による Entity Framework Core

docker を用いて SQL Server を構築し、簡単な Entity Framework Core のソースコードを実行します。

docker による SQL Server 構築

docker を用いて、最新の PostgreSQL を起動します。 簡単なパスワードだと失敗するため、注意してください。

$ docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=@Passw0rd" \
   -p 1433:1433 --name sql1 -h sql1 \
   -d mcr.microsoft.com/mssql/server:2019-latest

デフォルトではデータベース削除と作成に失敗するため、 新規にデータベース「ef」を作成します。

DBeaver などのツールから接続する場合、localhost で接続できますが、 C# から接続する場合、WSL2/Ubuntu 側の IP アドレスを指定する必要があります。 以下のコマンドを使って、IPv4 アドレスを取得します。

$ ip addr show dev eth0 | grep inet
    inet 172.18.93.234/20 brd 172.18.95.255 scope global eth0
    inet6 fe80::215:5dff:fe9e:379f/64 scope link

SQL Server 用のソースコード

SQLite の UseSqlite() を UseSqlServer() に変更し、 データベースの接続文字列を指定します。 このとき、ホスト名は「localhost」ではなく、 前述の ip コマンドで取得した IP アドレスを指定します。

NuGet によりインストールしたパッケージは、 Microsoft.EntityFrameworkCore.SqlServer 6.0.0 となります。

using Microsoft.EntityFrameworkCore;

await using var context = new SampleContext();

await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Samples.Add(new Sample() { Id = 12345, Name = "たびと" });
await context.SaveChangesAsync();

foreach (var item in context.Samples)
    Console.WriteLine($"{item.Id}: {item.Name}");

public class SampleContext : DbContext
{
    public DbSet<Sample> Samples { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlServer("Data Source=172.18.93.234,1433;Initial Catalog=ef;User ID=SA;Password=@Passw0rd");
}

public class Sample
{
    public int Id { get; set; }

    public string Name { get; set; }
}

実行すると以下の例外が表示されました。

SqlException: Cannot insert explicit value for identity column in table 'Samples' when IDENTITY_INSERT is set to OFF.

Id は自動採番されるようになっているようです。 詳細は Microsoft のサイトを参考にしてください。

SET IDENTITY_INSERT (Transact-SQL) - SQL Server | Microsoft Docs

ソースから、Id に値を設定している個所を削ります。

context.Samples.Add(new Sample() { Name = "たびと" });

これで正常に動作するようになりました。 Id は 1 からの自動採番となります。 これは、SQL Server 特有の現象で、他のデータベースでは発生しませんでした。

実行後に生成された SQL ServerDDL

ソースコードを実行した後に作成されるテーブル情報(DDL)は、以下の通りです。 Id には、自動採番用の IDENTITY プロパティが付いています。

CREATE TABLE ef.dbo.Samples (
    Id int IDENTITY(1,1) NOT NULL,
    Name nvarchar COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    CONSTRAINT PK_Samples PRIMARY KEY (Id)
);

Oracle による Entity Framework Core

docker を用いて Oracle を構築し、簡単な Entity Framework Core のソースコードを実行します。

docker による Oracle Express Edition (XE) 構築

今回は、 Oracle Database 21c Express Edition (XE) を構築します。 Oracle 21c XE の rpm と docker イメージを作成する資材をダウンロードし、 docker イメージを作成します。

昔はXE のダウンロードに認証が必要でしたが、 現在は認証なしでダウンロードできるようになりました。

$ mkdir oralce
$ cd oracle
$ git clone https://github.com/oracle/docker-images
$ cd docker-images/OracleDatabase/SingleInstance/dockerfiles/21.3.0
$ wget https://download.oracle.com/otn-pub/otn_software/db-express/oracle-database-xe-21c-1.0-1.ol8.x86_64.rpm
$ cd ..
$ ./buildContainerImage.sh -v 21.3.0 -x -i

docker イメージ完成まで、遅いマシンだと 30 分以上かかります。 イメージ完成後、Oracle 21c XE コンテナを起動します。

$ docker run -e "ORACLE_PWD=oracle" -p 1521:1521 --name oracle -h oracle -d oracle/database:21.3.0-xe

ここでも、データベースが使えるようになるまでしばらく待ちます。 以下のメッセージが表示されるまで、docker logs コマンドで実行ログを表示します。 終了するには、Ctrl + C キーを押してください。

$ docker logs -f oracle
~ 途中省略 ~
#########################
DATABASE IS READY TO USE!
#########################
~ 以下省略 ~

Oracleへの接続

DBeaver などのツールで、Oracle 21c XE に接続します。 今回は、SID の XE ではなく、PDB の XEPDB1 を使います。

  • Host: localhost
  • Port: 1521
  • Database: XEPDB1 (Service Name)
  • ユーザ名: sys
  • Role: SYSDBA

スキーマの追加

新しくスキーマ「ef」を作り、パスワードはコンテナと同じにします。 スキーマ作成後、SQL エディタ等で権限を与えます。

grant connect to ef;
grant resource to ef;
grant dba to ef;

以上で Oracle 側の準備は完了です。

Oracle 用のソースコード

SQLite の UseSqlite() を UseOracle() に変更し、データベースの接続文字列を記述します。

NuGet によりインストールしたパッケージは、 Oracle.EntityFrameworkCore 6.21.4 となります。

using Microsoft.EntityFrameworkCore;

await using var context = new SampleContext();

await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Samples.Add(new Sample() { Id = 12345, Name = "たびと" });
await context.SaveChangesAsync();

foreach (var item in context.Samples)
    Console.WriteLine($"{item.Id}: {item.Name}");

public class SampleContext : DbContext
{
    public DbSet<Sample> Samples { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseOracle("User ID=EF;Password=oracle;Data Source=localhost/XEPDB1");
}

public class Sample
{
    public int Id { get; set; }

    public string Name { get; set; }
}

実行後に生成された OracleDDL

ソースコードを実行した後に作成されるテーブル情報(DDL)は、以下の通りです。

CREATE TABLE "EF"."Samples" 
   (    "Id" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE, 
    "Name" NVARCHAR2(2000) NOT NULL ENABLE, 
     CONSTRAINT "PK_Samples" PRIMARY KEY ("Id")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS"  ENABLE
   ) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;

CREATE UNIQUE INDEX "EF"."PK_Samples" ON "EF"."Samples" ("Id") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;

他のデータベースは数行で割とイメージ通りに出力されましたが、 Oracle は過剰とも思えるぐらい大量に出力されました。

各データベースの特徴

前編・後編を通じて、それぞれのデータベースの特徴を記述します。

SQLite

  • NuGet インストールだけで、データベースを利用できる。
  • 運用管理端末など、チョッとしたツール作成時に重宝しそう。

PostgreSQL 14.1

  • 小文字が原則のようなので、UseSnakeCaseNamingConvention() が便利である。

MySQL 8.0.27

  • Docker コンテナにDBeaver等から接続するとき、「SSL、Allow public key retrieval」に気を付ける。
  • 本家か Pomelo の Entity Framework を使うべきかを悩む。

SQL Server 2019

  • Dokcer で、Linux版の SQL Server が利用できる。
  • Docker コンテナに C# から接続する場合、WSL2/Ubuntu の IP アドレスを指定する。
  • Int のキーを定義すると、自動採番プロパティが採用された。

Oracle Database 21c XE

  • Docker 用のイメージを作るのに時間がかかる。
  • Dokcer コンテナ起動後も、使えるまでに時間がかかる。
  • コードファーストで出力されたテーブルのDDLが大量に出力される。

まとめ

後編は、SQL ServerOracle を使って、.NET 6 の Entity Framework Core を作成した場合、 どれくらい違いがでるのかを確認しました。

データベースの削除と作成は、本番環境のアプリに組み込むのは危険ですが、 単体テストに組み込むと、データベースの操作が不要になるので便利です。

また、異なるデータベース間の移行ツールを Entity Framework Core で作るのも 以前の方法で作るよりも、簡単に作れそうで興味深いです。

最後に参考サイトを掲載しておきます。

では、皆さん、よい旅を。

参考サイト