検索
リンク
タグ
ASP.NET
.NET
ASP.NET MVC
F#
Visual Studio
Azure
ASP.NET Core
ライトニングトーク
Plone
Selenium
AJAX
C#
jQuery
SQL Server
ADO.NET Entity Framework
JavaScript
WebMatrix
EFCore
LINQ
Fizz-Buzz
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2024年 03月 2024年 02月 2024年 01月 2023年 12月 2023年 11月 2023年 10月 2023年 09月 2023年 08月 2023年 07月 2023年 06月 2023年 05月 2023年 04月 2023年 03月 2023年 02月 2023年 01月 2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 |
2020年 09月 24日
C# による、リレーショナルデータベース (今回は SQL Server が対象) にアクセスする、.NET Core 3.1 アプリケーションを開発していての話。 今回案件も、C# でリレーショナルデータベースを読み書きする際の定番ライブラリ EntityFramework Core (以下 EFCore) を採用。 EFCore のバージョンは 3.x だ。 EF Core のありがたさEFCore を使ったデータベースを読み書きするプログラムを書いていて大変助かると思う点のひとつは、データベース上のテーブルや列を、対応するクラスやそのプロパティとして記述するため、 「このテーブルは、プログラム中のどこで参照されているか?」 とか、 「この列は、プログラム中のどこで更新されているか?」 といったことが容易にわかることだ。 もちろんそれには、Visual Studio などのような、開発環境の支援 (例えば "Code Lens" 機能とか) があってのことでもある。 とにもかくにも上記のような機能・特性のおかげで、たとえばプログラムの不具合発覚時に修正作業を行なうときや、プログラムの変更などを行なうときに、大変効率良く短時間で済ませることができる。 生の SQL 文を書きたいときもあるさてさて、そうはいっても、希とはいえ、文字列として記述された生の SQL 文を実行したいケースは発生する。 そのようなケースは、EFCore が内部で生成する SQL 文では非効率なってしまうようなクエリを実行するためといったことも多いだろう。 今回自分が遭遇したのは、「大量のレコードを一括挿入したい」といったケースだった。 本来 (?) であれば、このようなケースでは、(対象が SQL Server なので) EFCore ではなく BULK INSERT 機能を使うべき、とかいろいろ対処のしようがあろうかと思う。 また、EFCore を使うとして、Add() して SaveChanges() するのを繰り返すにしても、適宜、オブジェクト追跡を切り離しながら消化していく方法も考えられる。 ※これをやらないで愚直に Add() ~ SaveChanges() を繰り返すと、1件挿入の処理時間が数十秒かかるようになったりする (いちどやらかしました)。 そもそも SQL Server データベースを格納先にしないという選択肢だってあるだろう。 ただ今回は、詳細はここでは明かせないが、なかなかに込み入った理由から、格納先は SQL Server 縛りで、EFCore でやるしかなく、また、オブジェクト追跡の切り離しもなかなか面倒であるという、レアケースにはまってしまった。 上記シチュエーションとなってしまったので、泣く泣く (?)、生の SQL 文 (INSERT 文) を実行することでレコード追加する実装とすることにした。 ExecuteSqlInterpolatedAsyncこのようなケースに対応できるよう、EFCore には、"ExecuteSqlInterpolatedAsync" というメソッドが用意されている。 このメソッドを使うことで、C# の "string interpolation" (文字列補間) 機能を活用して SQL インジェクションにならないようパラメータ指定しつつ、生の SQL 文を実行することが可能だ。 例えば以下のように任意の SQL 文を実行可能だ。
もっとも、生の SQL 文を使ってしまうと、冒頭で書いたような静的型付による恩恵を得られなくなる。 そのため、あくまでも泣く泣くであるが、"ExecuteSqlInterpolatedAsync" メソッドのお世話になることでどうにか事なきを得た。 SQL 文を実行した結果のスカラー値が欲しい!これで一件落着...と思ったのだが、さらなる要件が発生してしまった。 先の INSERT 文の実行なのであるが、その INSERT 文実行の結果の、払い出された Id 値の取得が必要となってしまったのだ。 これを実現するためには、まずは SQL 文を変更する必要がある。 昨今の SQL Server における SQL 文では、INSERT 文実行時に、払い出された Id 値が返るようにするには、以下のように "OUTPUT INSERTED.Id" という記述を書き足せば良いそうだ。
ここまではよい。 では上記 SQL 文を "ExecuteSqlInterpolatedAsync" に渡して実行すればそれよいのかというと、そうは上手くいかない。 "ExecuteSqlInterpolatedAsync" メソッドが返す戻り値は、SQL 文実行結果のスカラー値ではなく、SQL 文実行によって影響を受けたレコード件数であるからだ。 このケースで必要なのは、ADO.NET の層でいうところの ExecuteScalar に相当するその EFCore 版の機能・メソッドである・ ところが、ところがである。 自分が探した範囲では、どうにも、"ExecuteSqlInterpolatedAsync" に相当する、ただし SQL 文実行結果のスカラー値を返す版というメソッド・機能が、EFCore 内に見つけることができなかったのだ。 はてどうしたものか。 ADO.NET で書くの...?ちなみに、EFCore を使用していても、その下位層では ADO.NET が動いている。 そんなこともあり、直接 ADO.NET を使用したコードでこの課題を解決することも可能だ。 可能だ... のだが、しかし、そうするとなかなかにダルいコードになってしまう(下記)。
パラメータをせっせっと自分で組み立てるしかないとか、正直、気分が乗らない。 ExecuteSqlInterpolatedAsync の実装を見てみよう振り返って、"ExecuteSqlInterpolatedAsync" メソッドをよくよく見てみると、こいつはインスタンスメソッドではなかった。 拡張メソッドだったのである。 ということは、である。 "ExecuteSqlInterpolatedAsync" は、EFCore 深層部のプライベートな要素に依存せず、EFCore のパブリックな要素だけを相手に実装できているのではないだろか? ということは、もしかして、"ExecuteSqlInterpolatedAsync" のスカラー値返す版の拡張メソッドを自作するのは、実は容易なのではないか? そう思いついた。 そして幸い、EFCore は Apache 2.0 ライセンスの、GitHub 上にリポジトリを置くオープンソース製品である。 ということで、GitHub の EFCore のリポジトリを開き、"ExecuteSqlInterpolatedAsync" のソースコード (C#) を見てみた (下記リンク先)。 上記リンク先を見るとわかるように、予想どおり、"ExecuteSqlInterpolatedAsync" の実装は EFCore の非公開要素は使わずに、かなり少ない行数でできていた。 そしてその実装コード内で、IRelationalCommand インターフェースの ExecuteNonQueryAsync メソッドを呼び出して終了しているのを見つけた。 さらに調べてみると、IRelationalCommand インターフェースは、ExecuteNonQueryAsync のみならず、ExecuteReaderAsync や、そして ExecuteScalarAsync も用意されていたのだ! 以上のことから、ExecuteSqlInterpolatedAsync の実装コードをコピーしつつ、最後に ExecuteNonQueryAsync を呼び出しているところを ExecuteScalarAsync 呼び出しに差し替えた、自作の拡張メソッドを作れば、それで ExecuteSqlInterpolatedAsync のスカラー値を返す版が手に入るということだ。 オレオレ Execute"Scalar"SqlInterpolatedAsyncということで実際に作ってみたのがこちら。
ざっくりこれくらいの C# コードで拡張メソッドを書くことで、生の SQL 文を (C# の文字列補間を活用しつつ) 渡して実行し、実行した結果のスカラー値を戻り値として手に入れることができるようになった (下記はこの拡張メソッドを使用する例)。
警告が出てしまうのがちょっと残念であるが...なお、上記コードであるが、すこうし残念なことがある。 アクセス指定子が public ではあるものの、"Internal" 名前空間に存在する要素を参照していることだ。 このせいで、EFCore の NuGet パッケージに含まれる Analyzer によって、下記 EF101 警告が発生してしまう。
警告文にもあるように、"Internal" 名前空間にある要素は、予告なく仕様変更や削除されることがありえるよ、という訳だ。 言い換えると、先の自作の拡張メソッドは、EFCore の将来バージョン、それもたかだかパッチバージョンが変わっただけ程度でも、動作しなくなるかも知れない、そういうリスクがある、ということにある。 さてところで、本記事執筆時点では、EFCore は .NET 5 リリース候補第1版 と同じ 5.0.0-rc.1.20451.13 がリリースされている。 ということで、先のプログラムを、使用フレームワークを .NET Core 3.1 から .NET 5 に変更し、EFCore も、v.3.1 ではなく v.5.0.0-rc.1.20451.13 をパッケージ参照するに書き換えて、再ビルド・再実行してみた。 すると、これは嬉しい誤算! なんと、先の自作の拡張メソッドで参照していた、"Internal" 名前空間に棲息していた各要素は、EFCore v.5 RC1 では、いずれも独立した NuGet パッケージに切り出されて public API 扱いになったようなのだ! つまり、少なくとも EFCore のメジャーバージョンが上がるまでは、先の自作の拡張メソッドも安泰と考えてもよいだろう、ということだ。 これで晴れて・大手を振って、"ExecuteSqlInterpolatedAsync" のスカラー値を返す版の拡張メソッドを自作・活用してもよさそうだ。 実際に完動するコンソールアプリケーションのサンプルコードも、GitHub 上に上げておいたのでご参考までに。
by developer-adjust
| 2020-09-24 17:18
| .NET
|
Comments(0)
|
ファン申請 |
||