ODP.NET Core Managed Driver実装方法

Application Development .NET

.NET CoreからOracle Databaseに接続するには、ODP.NET Core Managed Driverを使います。さらに、これはOracle Clientのインストールなしで使用できます。

今回の記事ではC#のプログラムから、ODP.NET Core Managed Driverを使って、Oracle Database 19cに接続する方法、および詳細をまとめてみたいと思います。

ちなみに、今回の開発環境は以下の通り。

  • .NET 5.0 (C#)
  • Visual Studio 2019 for Mac
  • macOS Mojave
  • Oracle Database 19c(19.3)
  • ODP.NET Core Managed Driver

ODP.NET Core Managed Driverは、2020/11から.NET5.0をサポートしています。ただ、Oracleのオンラインドキュメントが少しごちゃごちゃしていて、最新のマニュアルを見つけるのに少し手間取りました。あと、例として書きやすいという理由で、C#のトップレベルステートメントで書いています。

補足ですが、Oracle Databaseを単にOracleと言うことにします(Oracleは社名で製品名ではない)。

なお、今回はOracle Linux上に19cのDBを構築していますが、構築方法は前回の記事で解説しています。

.NET 5.0に正式対応(2020/11)

↓のNews Releaseサイトにもありますが、ODP.NET Core Managed Driverは、Version 19.10から.NET 5.0に対応しています。

最新のオンラインドキュメントはから、見れます。現時点では、「ODAC 19c Release 2 (19.3.2)」が最新です。

ただ、開発者ガイドfor Microsoft Windowsにも同様の記載があり、そちらでは「NET Core 3.xは現在サポートされていません」と書かれています(バージョンが古いのでしょうが、紛らわしい…)。

ちなみに、公式twitterでも

とアナウンスされていますので、対応はしているのは間違いなさそうですね。

思い切ってtwitterでOracleの中の人に確認してみたところ、開発者ガイドは19.3ではない19cのもので、その時は.NET Cocre 3.xに対応していなかったのだそうです。

いや、マニュアルの整理がちょっとできてないんじゃね?とは思うのですが。

(補足)Oracle公式とMicrosoftで日本語訳が違う

Oracleマニュアルの開発者ガイドfor Microsoft Windowsを見てもらえば分かりますが、Oracle Data Provider for .NET管理対象ドライバとか管理対象外ドライバという言い方がされています。

恐らく訳者の方がご存じなかったのだと推測しますが、Microosftの.NETのサイトだと、マネージドとかアンマネージドと表現されているものが、これにあたります。英語の”managed”で、CLRで管理されるので「管理対象」という訳語は意味は間違ってませんが、マイクロソフトの日本語サイトでも”マネージドコード”という表現がされているので合わせて欲しい気はしますが、とにかく日本語訳が違っています(Google検索で引っかかりにくくなるしね…)。

Wikipediaで恐縮ですが、Managed Codeのページにも、”The term was coined by Microsoft.(この用語はMicrosoftにより造語された)”とあります。一般的な名称というよりは、Microsoftだけで使う用語のようです。

ちなみに、Javaだと同じ位置づけのものは「バイトコード(bytecode)」ですかね?

ちなみに、この記事では、特に必要が無い限りは「Managed」とかマネージドという表現をします。

環境の準備

ODP.NET Core Managed Driver追加

NuGetから、Oracle.ManagedDataAccess.Coreを探し、「パッケージの追加」でインストールします。

ODP.NET Managed Driver

接続先情報の追加

ODP.NET Core Managed Driverは、.NETの構成ファイル(app.config、web.config、machine.config)をサポートしていません。補足ですが、.NET Framework用のODP.NET Managed Driverでは構成ファイルをサポートしています。

可能な方法は以下の3つ。

  • コード中で接続文字列を指定
  • .NET構成API(OracleConfiguration)を使用
  • tnsnames.oraを使用

以下の実装例で実際のコードを書いてみます。

ODP.NET Core Managed Driver 実装例

普通は抽象化してDAO (Data Access Object)を作成するかもしれませんが、今回は直書きします。あと、ネストが深くなると見にくいと思ったので、usingを使用していません。

まずは、接続の方法から。

接続方法1:コード中で接続文字列を直接記述

tnsnames.oraがなくても接続できます。まぁ、以前からある方法だと思います。

using System;
using Oracle.ManagedDataAccess.Client;

var connectionString = "user id=scott;password=tiger;data source=192.168.1.101/pdb1";
OracleConnection pdb1Con = null;

try
{
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();
    Console.WriteLine("Success");
}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();
}

接続方法2:.NET構成API(OracleConfiguration)

静的クラスのOracleConfigurationを使用します。

using System;
using Oracle.ManagedDataAccess.Client;

//接続識別子
OracleConfiguration.OracleDataSources.Add("pdb1", "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.101)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=pdb1)(SERVER=dedicated)))");

var connectionString = "user id=scott;password=tiger;data source=pdb1";
OracleConnection pdb1Con = null;

try
{
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();
    Console.WriteLine("Success");

}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();
}

また、OracleConfigurationで詳細な設定が可能です。詳細は、以下。

接続方法3:tnsnames.ora

tnsnames.oraの検索の優先順位は以下。

  • OracleConfiguration.TnsAdminプロパティで設定されたディレクトリ
  • 実行中ODP.NET Coreアセンブリのディレクトリ
  • 現行作業ディレクトリ

実行時フォルダに以下のtnsnames.oraがあるとします。

ODP.NET Managed Driver
PDB1 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.101)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = pdb1)
    )
  )

コードはほぼ同じで、接続情報の部分がないだけです。

using System;
using Oracle.ManagedDataAccess.Client;

var connectionString = "user id=scott;password=tiger;data source=pdb1";
OracleConnection pdb1Con = null;

try
{
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();
    Console.WriteLine("Success");

}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();
}

上でも説明しましたが、OracleConfiguration.TnsAdminでtnsnames.oraがあるディレクトリを指定する方法でも可能です。

using System;
using Oracle.ManagedDataAccess.Client;

//tnsnames.oraがあるディレクトリを指定
OracleConfiguration.TnsAdmin = "/tmp";

var connectionString = "user id=scott;password=tiger;data source=pdb1";
OracleConnection pdb1Con = null;

try
{
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();
    Console.WriteLine("Success");

}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();
}

データ取得

基本は以前のODP.NETと変わらないはずです。
(コードが汚かったら、教えて下さい)

using System;
using Oracle.ManagedDataAccess.Client;

//tnsnames.oraのディレクトリを指定
OracleConfiguration.TnsAdmin = "/tmp";

var connectionString = "user id=scott;password=tiger;data source=pdb1";
OracleConnection pdb1Con = null;
OracleCommand pdb1Cmd = null;
OracleDataReader rdr = null;

try
{
    //接続
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();

    if (pdb1Con.State == System.Data.ConnectionState.Open)
        Console.WriteLine("Successfully Connected.");

    //DBの日時を取得
    pdb1Cmd = pdb1Con.CreateCommand();
    pdb1Cmd.CommandText = "select to_char(sysdate,'YYYY/MM/DD HH24:MI:SS') from dual";
    rdr = pdb1Cmd.ExecuteReader();

    //出力
    while (rdr.Read())
        Console.WriteLine("Time: " + rdr.GetString(0));
}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();

    if (pdb1Cmd != null)
        pdb1Cmd.Dispose();

    if (rdr != null)
        rdr.Dispose();
}

更新

これも、従来から同じですかね。ExecuteNonQueryで実行します。

using System;
using Oracle.ManagedDataAccess.Client;

//tnsnames.oraのディレクトリを指定
OracleConfiguration.TnsAdmin = "/tmp";

var connectionString = "user id=scott;password=tiger;data source=pdb1";
OracleConnection pdb1Con = null;
OracleCommand pdb1Cmd = null;
OracleDataReader rdr = null;

try
{
    //接続
    pdb1Con = new OracleConnection(connectionString);
    pdb1Con.Open();

    if (pdb1Con.State == System.Data.ConnectionState.Open)
        Console.WriteLine("Successfully Connected.");

    //データ追加
    pdb1Cmd = pdb1Con.CreateCommand();
    pdb1Cmd.CommandText = "insert into staff (id, name) values (100, 'Scott')";
    var rtn = pdb1Cmd.ExecuteNonQuery();

    //取得
    pdb1Cmd.CommandText = "select id, name from staff where name = 'Scott'";
    rdr = pdb1Cmd.ExecuteReader();

    //出力
    while (rdr.Read())
        Console.WriteLine($"id: {rdr.GetInt16(0)}, name:{rdr.GetString(1)}");

}
catch (Exception ex)
{
    Console.WriteLine($"Connection Failed: {ex.Message}");
}
finally
{
    if (pdb1Con != null)
        pdb1Con.Close();

    if (pdb1Cmd != null)
        pdb1Cmd.Dispose();

    if (rdr != null)
        rdr.Dispose();
}

個人的にポイントだと思ったところ

旧来のアンマネージドなODP.NETに比べ、DLLの数もファイルサイズも少ないのですっきりはしますし、クライアントをインストールしなくていいので、開発も容易になりそうです。

あと、クラスやメソッドなどAPIがまったく同じなので、混乱もしないでしょう(名前空間がOracle.ManagedDataAccessという違うくらい)。

ただ、ODP.NET Managed DriverとODP.NET Core Managed Driverでは利用可能なクラスが異なるようなので、移行する時は検証が必要となりそうです(対応表は以下のリンク参照)。

以上です。

参考リンク