C#でFirebirdのストアドを書く
本エントリは、Firebird AdventCalender 2016 の14日目です。
今回は、Firebirdの.NET ProviderのコミッターであるJiri Cincura氏がブログで公開している、FbNetExternalEngineについて書きます。
目次
FbNetExternalEngineってなに?
簡単に言うと、C#でFirebirdのストアドがかけるようなpluginです。
Firebird3では新しいplugin機構が追加され、ストアドや関数、トリガー等がFirebirdの内部エンジン以外で動作できるようになりました。
基本的にはC++でpluginを書くことが想定されているようです。
そんな中Jiri Cincura氏が、C#用のplugin機構を仮実装したのが「FbNetExternalEngine」です。
ちなみに、procedureだけの対応です。
もっともお試し実装なので、Productionでは使えません。
簡単な例
実際にどんな風になるかというと。
こんなC#の関数を用意して、
public static IEnumerator<Tuple<string>> HellowWorld(string name) { yield return new Tuple<string>($"Hello World!!, Hello {name}!!"); }
SQL投げると、書いた処理が返ってくる。
実装
Firebird Embeddedで実際に試してみた。
検証環境
- C# (.NET 4.6.1)
- Firebird .NET Data Provider 5.6.0
- Firebird 3.0.0 (Embedded)
Firebirdのバージョンが古いですが、手元にあったのでそのままw
Pluginの作成
ポイントは以下の通り。
- static クラス
- static メソッド
- 戻り値はIEnumerator
IEnumerableではありません!私はよく間違えて動かない (゚A゚;)ってなってます。 - 引数は必ず用意する
引数無しだと上手く動かせなかった... - 引数/戻り値がPrimitive&Value Typeの場合死ぬので、Nullable
にする
こう見ると、割と制限が多い。
サンプル
namespace FbExternalSample { public static class Procedures { public static IEnumerator<Tuple<string>> HellowWorld(string name) { yield return new Tuple<string>($"Hello World!!, Hello {name}!!"); } public static IEnumerator<Tuple<int?>> GetNumbers(int? count) { return Enumerable.Range(0, count.Value).Select(x => Tuple.Create((int?)x)).GetEnumerator(); } public static IEnumerator<Tuple<int?, string>> GetDemo(string txt) { yield return new Tuple<int?, string>(1, "sample1"); yield return new Tuple<int?, string>(2, "sample2"); yield return new Tuple<int?, string>(3, $"{txt}"); yield return new Tuple<int?, string>(4, $"Done!!"); } } }
Pluginを呼ぶほう
まずは普通にEmbeddedが動く環境整備
適当に必要なDLLを配置する準備だけしておきます。
よくわからなければ、過去記事を参照。
Firebird3.0でEmbedded試してみた - ぽにょろん
FbNetExternalEngineを配置
下記ブログ記事から、必要なアセンブリを取得します。
External procedures in Firebird in .NET done(-ish) | tabs ↹ over ␣ ␣ ␣ spaces by Jiří {x2} Činčura
ちなみに、7zで圧縮されてます。解凍して、中身をpluginsフォルダに突っ込みます。
plugins.confの作成
Firebirdのplugin設定を変更するために、plugins.confを配置又は作成します。
中身はこんな感じで。
Plugin = FBNETEXTERNALENGINE { Module = $(dir_plugins)/FbNetExternalEnginePlugin Config = FBNETEXTERNALENGINE_config } Config = FBNETEXTERNALENGINE_config { }
DBにprocedure作成しておく
FirebirdのDBファイルを適当に作成して、そこにprocedureのcreate文を流し込みます。
recreate procedure HellowWorld (in_name varchar(300)) returns (out_0 varchar(300)) external name 'FbExternalSample!FbExternalSample.Procedures.HellowWorld' engine FbNetExternalEngine; recreate procedure GetNumbers (in_count integer) returns (out_0 integer) external name 'FbExternalSample!FbExternalSample.Procedures.GetNumbers' engine FbNetExternalEngine; recreate procedure GetDemo (in_txt varchar(300)) returns (out_0 integer,out_1 varchar(300)) external name 'FbExternalSample!FbExternalSample.Procedures.GetDemo' engine FbNetExternalEngine;
作成したPluginを配置する
上記で作成した、PluginのDLLもFirebirdのアセンブリがあるPluginsフォルダにぶち込みます。
実行
あとは、SQLを実行するだけ。
結構雑なコードですけど、こんな感じで確認すると、
var sqls = new[] { "SELECT * FROM HellowWorld('Taro')", "SELECT * FROM GetNumbers(5)", "SELECT * FROM GetDemo('やったぜ!')" }; using (var con = new FbConnection(builder.ConnectionString)) using (var command = con.CreateCommand()) { con.Open(); foreach (var sql in sqls) { Console.WriteLine("- SQL -"); Console.WriteLine(sql); Console.WriteLine("- 実行結果 -"); command.CommandText = sql; var reader = command.ExecuteReader(); while (reader.Read()) { var result = ""; for (var i = 0; i < reader.FieldCount; i++) { result += $" {reader[i]}"; } Console.WriteLine(result); } Console.WriteLine("------------------------------------"); } }
こんな結果になります。
最後に
Jiri Cincura氏のブログ読んで貰えればわかりますが、実際はまだまだ試作段階です。
制限も多いし手順も割と面倒ではありますが、まぁ、とりあえずできるよ!という状況です。
とはいえ、どれくらい存在するのかわからない、Firebird + C#erには嬉しいので、色々と試してみてはいかがでしょうか。
記事を書くのに殴り書いたサンプルも置いておきます。
Sample/FbExternalSample at master · kowill/Sample · GitHub