2018年7月16日月曜日

今一度 ADO.NET について考えてみる

.NET のデータアクセス API である ADO.NET について、私が普段考えていること実施していることをまとめてみます。




共通型の使用


ビジネスロジックがプロバイダの実装に依存してしまうのを避けるため、SqlDbConnection や OleDbConnection などの各プロバイダで定義された型ではなく、できるだけ DbConnection や IDbConnection といった共通型を使用するようにしています。

ODP.NET の OracleCommand.BindByName プロパティなどプロバイダ固有の機能を呼び出す場合は、共通メソッドを定義するなどしてキャストを含む操作を行う箇所をできるだけ局所化します。例えば BindByName プロパティの場合は「新規コマンドを生成して返す」や「指定されたコマンドに初期値を設定する」共通メソッドを定義し、その中でコマンドを OracleCommand にキャストして BindByName プロパティに true を設定します。


ログ出力


実行した SQL や実行時間を記録するためのログを出力できるようにします。SQL を実行するたびにいちいちログ出力を実装しなくてもよいように、「指定されたコマンドを実行する」共通メソッドを定義し、その中でログ出力を実装します。

ログ出力に限らず、汎用的な処理を割り込ませたい場合にラッパークラスを用いる方法もあります。ADO.NET のラッパーはいざ実装するとなるとややこしいことが多いのですが、AdoNetProfiler というライブラリを見つけました。コネクション/トランザクション/コマンドの一通りの動作に対するフックをかけることができます。私はまだ Npgsql で試している段階ですが、利用するための手順も少なく導入しやすいのではないかと思います。今は一部のコマンド実行のみログを出力したいような場合を想定したフィルタリングの手段を考えています。


ADO.NETの処理を診断するAdoNetProfiler - Qiita



O/Rマッピング


様々なライブラリが存在していますが、私個人は薄い MicroORM で SQL 文を直書きするのがベターであると考えています。

抽出だけでなく更新もサポートしているような(重厚な)ORM は単一のテーブルに対する CRUD 操作は確かに効率的であるのですが、それ以外のケースでは柔軟性に欠けます。実際に発行する SQL 文の大半は結合や集計を伴います。

自動生成された SQL 文はパフォーマンス面の問題があることも多く、どのような SQL が発行されているのかを調べるのも簡単ではなかったりします。「パフォーマンスに問題がありそうな場合は SQL 文を直書きし、そうでない場合はマッピングの機能を利用する」といった開発ルールを設けたとしても、その閾値は簡単に決められませんし、プログラマのスキル(やモラル)に左右されます。開発終盤や運用開始後にパフォーマンス問題に直面する可能性が高いと感じています。

上記の理由で、LINQ to SQL はあまり使いません。LINQ to Object は使います。



非同期処理



SqlCommand では BeginExecuteNonQuery などの非同期メソッドが提供されていますが、IDbCommand では提供されていません。DbCommand にはありますが、実装は各プロバイダごとによって異なります。前述の通り、私はできるだけプロバイダ固有の機能を使わない方針であるため、これらの非同期メソッドを使用することはあまりありません。

共通型を用いた非同期処理を実装することを考えたこともあります。しかし一つのコネクションで複数のコマンドの実行をサポートしていないプロバイダがある以上、同時実行を防ぐためにブロックしたり、別のコネクションで処理させたりするような実装にならざるを得ず、メリットが小さいと考えて実装を保留しました。もしメリットが大きいのであれば、そのようなライブラリがリリースされてコモディティ化しているだろうとも思います。


機能の拡張



ADO.NET は初期の .NET Framework から存在しているだけあって、機能的には古さを感じます。過去のバージョンとの互換性の担保や各プロバイダごとの機能差異を考えると、大きく機能が追加される可能性は低いのではないかと思っています。

汎用的な機能を追加したい場合、ラッパークラスやユーティリティクラス、拡張メソッドを実装することが多いです。 最近は拡張メソッドを用いることが多いのですが、同様に拡張メソッドを用いる他のライブラリを併用していると、同じような名前のメソッドが増えて使いづらいこともあります。ユーティリティクラスにメソッド本体を定義し、そのメソッドを呼び出す拡張メソッドを定義するようにしています。ユーティリティクラスの名前空間と拡張メソッドの名前空間を分けておき、使いたいほうの名前空間を using するようにしています。

0 件のコメント:

コメントを投稿

paiza のスキルチェックをやってみました

いまさら感はありますが、 paiza のスキルチェックをやってみました。指定された時間内にコードを書いてユニットテストにかけ、その結果を基に評価を数値化してくれるというものですが、ゲーム感覚で空き時間を見つけて進めていこうと考えています。 どうやら時間が短いほど高い評価を得...