Oracle Database 21c で LINQ を使う準備

Hyper-V の Windows Server に Oracle 21c を入れて、LINQ が使えるようにするところまでのメモ書きです。

Oracleは 11g 以来(実務だと9i あたりか)なので、途中にかなり間があいていますが、インストールとかデータベース作成とか落とし穴的なところは以前と変わらず。ちなみに、Java で作ってある超遅いインストーラは 21c から抜群に早くなっています。平行して試した 19c だと遅いままなので、おそらく 21c から最適されていた模様です。

おそらくお手軽なのは、Oracle Cloud を使うようなのですが、今回は訳あってオンプレミスな環境でテストをします。

仮想環境の構築

  • Hyper-V 上に Windows Server 2022 をインストール
  • メモリを 8GB、ストレージを 64GB 割り当て
  • ストレージは、ホストの SSD 上に作成する(スピードが2倍位違います)

動かしてみると常時 5GB 程度のメモリを使っているので、8GB 程度にするほうがよさそうです。ただし、Oracle 21c インストール時に「メモリが足りないけど続行するか?」のメッセージが出るので、余裕があれば 16GB 程度割り当てたほうがよさそう。が、実際のところ、仮想環境に 16GB 割り当てるのは難しそうなので、8GB 割り当てます。

Azure などのクラウド上で Windows Server を構築する場合、メモリが 8GB になると結構な課金が発生するので注意してください。4GB だと、インストール時に結構辛いかもしれません。

試験環境での実験なので、Express ではなく本家のものを使います。

Oracle Database 21c Download for Microsoft Windows x64 https://www.oracle.com/database/technologies/oracle21c-windows-downloads.html

以前は製品版を使うとライセンスサーバーを必要としていたのですが、今はどうなのだろう?

.NET6 の環境構築のための Visual Studio 2022 を入れます。

Visual Studio 2022 コミュニティ エディション – 最新の無料バージョンをダウンロードする https://visualstudio.microsoft.com/ja/vs/community/

dotnet & Visual Studio Code で軽量化することも可能ですが、NuGet の扱いとかデバッグ出力とかの扱いで面倒がないので VS2022 を入れてします。

Oracle Database 21c のインストールと初期設定

実は、Oracle 21c からプラカブルデータベースが必須になりました。インストールの選択に非PDBの選択肢がなくなっています。

  • CDB:コンテナデータベース
  • PDB:プラカブルデータベース

Oracle の場合、「データベース」という名の付くものが色々あって結構ややこしいです。

SQL Server の場合、インスタンス > データベース >テーブルのように比較的単純なのですが、

Oracle の場合、グローバルデータベース(インスタンス)>コンテナ(コンテナデータベース)>プラカブデータベース>テーブル という形で、いたるところで「データベース」が出てきます。

以前、あったテーブルスペース(表領域)はどこにいったのでしょう?という疑問があるのですが、これは後で調べます。スクリプトでデータベース(プラカブデータベース)を作り限り、従来通りの TABLESPACE も使えているようです。

インストール時に「ソフトウェアのみインストールする」にして、あとからデータベースを作成しています。

  • グローバルデータベース:orcl.mshome.net
  • プラカブルデータベース:orcldb

sqlplus / as sysdb で、次の状態まで確認します。

ORALCE_BASE と ORACLE_HOME

ORACLE_BASE を c:\app\oracle にして作成したのですが、tnsnames.ora 等のファイル位置が 19c と 21c では異なっています。ORACLE_HOME に下にはありません。

tnsnames.ora 等のファイルは以下にあります。

C:\app\oracle\homes\OraDB21Home1\network\admin

どうやら、$ORACLE_BASE/homes 配下に作成されるようです。

以下の例では、

  • ホスト名:oracle21sv
  • Oracle で設定される?サーバー名:oracle21sv.mshome.net
  • サービス名:orcldb.mshome.net
  • システム識別子(SID):ORCL

となっています。

listener.ora

# listener.ora Network Configuration File: C:\app\oracle\homes\OraDB21Home1\NETWORK\ADMIN\listener.ora
# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = CLRExtProc)
      (ORACLE_HOME = C:\app\oracle\product\21.3.0\dbhome_1)
      (PROGRAM = extproc)
      (ENVS = "EXTPROC_DLLS=ONLY:C:\app\oracle\product\21.3.0\dbhome_1\bin\oraclr.dll")
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = oracle21sv.mshome.net)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )

tnsnames.ora

# tnsnames.ora Network Configuration File: C:\app\oracle\homes\OraDB21Home1\NETWORK\ADMIN\tnsnames.ora
# Generated by Oracle configuration tools.

ORACLR_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
    (CONNECT_DATA =
      (SID = CLRExtProc)
      (PRESENTATION = RO)
    )
  )

ORCL =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = orcldb.mshome.net)
    )
  )

このあたりの設定がややこしいので、

  • lsnrctl status
  • tnsping <システム識別子(SID)>

を活用して事前にチェックしておきます。

うまく TNS の設定ができていれば tnsping orcl がで tnsnames.ora の設定が取得できます。

確か、 listener.ora と tnsnames.ora を手作業で修正した場合は、lsnrctl を再起動させます。

  • lsnrctl start
  • lsnrctl stop

データベースの作成

昔ながらのテーブルスペースを使った方式です。これいいかわからないので、これは後で修正。

CREATE TABLESPACE REDMINETS DATAFILE 
 'C:\app\oracle\oradata\ORCL\DATAFILE\REDMINETS.dbf' 
  SIZE 1G AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED;

CREATE USER redmine IDENTIFIED BY redmine 
  DEFAULT TABLESPACE REDMINETS 
  TEMPORARY TABLESPACE TEMP 
  ACCOUNT UNLOCK ;

GRANT UNLIMITED TABLESPACE TO redmine;
GRANT CREATE SESSION TO redmine;
GRANT CONNECT TO redmine;
GRANT RESOURCE TO redmine;
ALTER USER "REDMINE" DEFAULT ROLE ALL;

redmine という名前でユーザーを作成しておきます。

  • 表領域 REDMINETS
  • ユーザー名 redmine(内部では自動的に大文字になるので、正確には REDMINE です)
  • パスワード redmine

権限が少し過剰ですが、ひとまずこれで Ok です。この使い方は、いまとなっては少しイリーガルなので、プラカブルデータベースを使った方式に直します。

この状態で、sqlplus でログインできるようになります。

sqlplus redmine/redmine@orcl

最後の orcl は、tnsnames.ora に設定したシステム識別子(SID)を使います。

tnsnames.ora が間違っている場合は、「ORA-12154: TNS: 指定された接続識別子を解決できませんでした」のエラーがでます。

ユーザー名あるいはパスワードが間違っている場合は「ORA-01017: invalid username/password; logon denied」です。

ほかにも、サービス名が間違っている、サーバー名が間違っている、場合もあるので、lsnrctl status と tnsping を使ってチェックしていきます。

テーブルの作成

テスト用の Books テーブルを作成します。

create table books (
  id number(16) not null,
  title varchar2(20),
  author varchar2(20),
  publisher varchar2(20)
);

仮データの挿入

insert into books values ( 1, 'Oracle入門', 'A', '出版社' );
insert into books values ( 2, 'SQL Server入門', 'B', '出版社' );
insert into books values ( 3, 'MySQL入門', 'A', '同人誌' );

テストプロジェクトの作成

接続確認をするプロジェクトを作成します。

dotnet new console --name DbConnectSample

NuGetで、ライブラリを追加

  • Microsoft.EntityFrameworkCore
  • Oracle.EntityFrameworkCore ODP.NET EF Core
  • Oracle.ManagedDataAccess.Core ODP.NET Core
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.12" />
        <PackageReference Include="Oracle.EntityFrameworkCore" Version="5.21.4" />
        <PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.4" />
    </ItemGroup>
</Project>

Oracle.EntityFrameworkCore と Microsoft.EntityFrameworkCore のバージョンを 5 で揃えておきます。これは現時点では、 Oracle.EntityFrameworkCore は EF Core 5 までしか対応していないためです。

Oracle.ManagedDataAccess.Core は Oracle への接続のために使っています。

実験コードは以下の通りです。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
using Oracle.ManagedDataAccess.Client;

Console.WriteLine("Hello, Oracle World!");
Console.WriteLine("use DbContext");
var db = new OracleDbContext();
var items = db.Books.ToList();
foreach (var it in items)
{
    Console.WriteLine($"{it.ID} {it.TITLE}");
}

public class OracleDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var builder = new OracleConnectionStringBuilder();
        builder.UserID = "redmine";
        builder.Password = "redmine";
        builder.TnsAdmin = @"C:\app\oracle\homes\OraDB21Home1\network\admin";
        builder.DataSource = "ORCL";
        string cnstr = builder.ConnectionString;
        optionsBuilder.UseOracle(cnstr);
    }
    public DbSet<BOOKS> Books => Set<BOOKS>();
}

/*
SQL> desc books ;
 名前                                                  NULL?    型
 ----------------------------------------------------- -------- ------------------------------------
 ID                                        NOT NULL NUMBER(16)
 TITLE                                              VARCHAR2(20)
 AUTHOR                                             VARCHAR2(20)
 PUBLISHER                                          VARCHAR2(20)
 
 */
[Table("BOOKS")]    // これが必須
public class BOOKS
{
    [Key]
    public Int64 ID { get; set; }
    public string TITLE { get; set; } = "";
    public string AUTHOR { get; set; } = "";
    public string PUBLISHER { get; set; } = "";
}

Oracle のほうではテーブル名やカラム名を大文字で保持しているようなので、エンティティクラスのほうも大文字にしておきます。ただし、テーブル名はなぜかそのままでは認識できず Table 属性で指定します(おそらく ODP.NET EF Core のほうの不具合ような気も)。

実行結果

大文字のまま扱ってもよいのですが、C# の命名規約上のちのちややこしいことになるので、Column 属性で変更しておきます。

[Table("BOOKS")]
public class Books
{
    [Key]
    [Column("ID")] public Int64 Id { get; set; }
    [Column("TITLE")] public string Title { get; set; } = "";
    [Column("AUTHOR")] public string Author { get; set; } = "";
    [Column("PUBLISHER")] public string Publisher { get; set; } = "";
}

これでうまくC#の命名規則にあわせられます。

カテゴリー: 開発 パーマリンク