検索
リンク
タグ
ライトニングトーク
Plone
Selenium
ASP.NET
ASP.NET Core
SQL Server
ADO.NET Entity Framework
LINQ
AngularJS
JavaScript
WebMatrix
AJAX
.NET
Fizz-Buzz
F#
ASP.NET MVC
jQuery
Visual Studio
C#
Azure
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
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月 ファン
ブログジャンル
画像一覧
|
2019年 11月 22日
EFCore の "所有されているエンティティ型".NET プログラミングにおけるデータベースアクセスライブラリの定番である、EntityFrame Core (以下、EFCore)。 EFCore を用いたデータベースを読み書きするプログラム開発において用いられる技法のひとつとして、"Code First" と呼ばれる作り方がある。 すなわち、EFCore を基盤とした C# ソースコードとしてデータベースのテーブル構造を記述してプログラムをビルドし、そのプログラムの実行によって、データベースとその構造を自動生成するというものだ。 自分は趣味/仕事の双方で、この "Code First" スタイルのプログラム開発を行なっている。 さてそのような "Code First" スタイルでの EFCore プログラミングにおいて、"所有されているエンティティ型" という機能が EFCore に用意されている。 これは何か、ちょっとサンプルコードを示してみよう。 まず、データベース上で People テーブルにマップすることにする、Person クラスというのを C# ソースコードとして記述する。
ところで、このプログラム開発において、Height と Weight の 2つのプロパティのペアが、この Person クラスに限らず他の多数のクラス (=テーブル) でも頻出するとしよう。 その場合、Height と Weight の 2つのプロパティを持つ独立したクラスを "所有されているエンティティ型" として作成し (ここでは Metric クラスとする) 各所で再利用できるようになる。
こうして実装したプログラムを実行し、データベースを自動生成されると、次のような People テーブルを作成してくれるのである。 ![]() "所有されているエンティティ型" を使った実装について、このサンプルコードだと今ひとつ効能がわかりにくいかもしれない。 しかし、同じセンサーデータ構成をセンサーの数だけ同一レコード上に列として並べたい場合など、効率良く且つミスなく実装できるので便利なのである。 なお、"所有されているエンティティ型" は他にもいくつかその自動生成されるデータベース構造にバリエーションがあるのだが、自分はもっぱら上記サンプルコードで示したようなパターンで利用している。 その他、"所有されているエンティティ型" について詳しくは、公式ドキュメントを参照されたし。 EFCore の v2.x と v.3.0 とで生成されるテーブル構造が異なる!...と、まぁ、このような "所有されているエンティティ型" を活用したプログラム開発を、EFCore の ver.2.1~2.2 ベースで行なってきた。 そしてあくる日。 EFCore の次バージョン、ver.3.0 がリリースされたので、手持ちのプロジェクトの EFCore のバージョンを、ver.2.x から ver.3.0 に更新した。 そして動作を確認していたところ、"所有されているエンティティ型" に関して、自動生成されるテーブル構造が変わっていることに気がついた。 まずおさらいだが、EFCore v2.x においては、最初に示したサンプルコードだと、Height および Weight は、C# コード上は double 型なので、自動生成されるデータベース上の型は float NOT NULL というように非 null 許容となっている。 (以下にデータベース構造の図を再掲する) ![]() これは少なくとも自分にとっては直感と相違ない振る舞いである。 もしデータベース上の型を null 許容としたければ、C# コード側を double? と記述することになる。 ところが、である。 EFCore v3.0 だと、同じコードからデータベースを自動生成するようにすると、なんと、C# コード上、"所有されているエンティティ型" に含まれる double 型が、データベース上では float NULL というように null 許容になってしまったのだ(下図)。 ![]() これが、"所有されているエンティティ型" に含まれるプロパティではなく、テーブルにマップされるクラス直属のプロパティであれば、これまでと変わらない振る舞いとなる。 すなわち、C# コードの double 型はデータベース上の float NOT NULL に、double? 型は float NULL となるようにデータベース構造が生成される。 この "所有されているエンティティ型" に含まれるプロパティが null 許容になってしまう振る舞いは、現時点での最新バージョン (但し安定版ではない) である v.3.1 Preview 3 や、安定版の最新パッチバージョン v.3.0.1 でも同じであった。 破壊的変更があるのは仕方がないが...まぁ、EFCore も v.2 から v3 へとメジャーバージョンがあがるくらいなので、破壊的変更があるのは仕方がないとは思う。 また、もう既にこの振る舞いで ver.3.0 として安定版リリースされていることもあるだろうから、今更 ver.2.x と同じ動作に戻すことも無理があろう。 しかしである。 ちょっと問題視してる点、というかホントに困った点は、いろいろ調べた限りでは、どうも、"所有されているエンティティ型" に含まれるプロパティを、明示的に非 null 許容としてデータベース生成する方法がなさそうなのだ。 あちこちに Required 属性をつけたり、OnModelCreating() メソッド内で fluent API であれこれモデルの指定を行なっても、 "所有されているエンティティ型" に含まれるプロパティが null 許容になってしまう動作は変えられなかった。 唯一の回避策としては、データベースマイグレーションの仕組みを使ってデータベース自動生成の処理を C# ソースファイルとして生成し、その自動生成されたマイグレーション用の C# ソースファイルを手で編集して、nullable: true になってる箇所を nullable: false に書き直してくれ、というのである (出典は下記)。 これはちょっとあんまりではないかと個人的には思われるのだが、どうだろうか。 EFCore は Apache License 2.0 のオープンソース製品であり、リポジトリは GitHub にある。 そして EFCore の GitHub リポジトリ上には本件に関する Issue があがっており(下記 #12100)、いちおう EFCore の開発チームはこの問題(?)を承知しているようではある。 また、必要な人はこの Issue (#12100) に投票しよう、との呼びかけもある。 とはいえ、1,000 を超える Issue が開かれたままであり、また、この問題についての Issue についての Vote (投票) 数も目立って多いとはいえず、近い将来の内にも何かしらの対応がなされるのかどうかはあまり期待が持てないでいる。 最後にこの問題(?)を再現するソースコード一式は下記にて公開している。 EFCore の ver.3.x は、ver.2.x に比べていろいろ魅力的な改善が施されており、早く移行したいところではある。 しかし自分がたずさわっているプロダクトではこの件がネックになって、未だに EFCore ver.2.x を使用したまま足踏みしている。 同じ悩みを抱えている方々はいないのだろうか、あるいは、どのように回避されているのであろうか、気になる次第。
#
by developer-adjust
| 2019-11-22 20:14
| .NET
|
Comments(0)
2019年 11月 07日
巨大なテキストファイルでも1行ずつなら処理可能!.NET Core 3.x 上の C# でのプログラミングにおける話。 改行で区切られた行指向のテキストファイルを、1行ずつ読み取っては何か処理をする、といった場合、自分がよくお世話になるのは、System.IO 名前空間の File クラスに備わっている ReadLines() 静的メソッドだ。 このメソッドに目的のテキストファイルのパスを渡して呼び出してやれば、そのテキストファイルの1行1行を文字列に読み込んで列挙する IEnumerabe<string> が返る。 あとはその返された IEnumerabe<string> を使って、foreach で列挙を回したり、LINQ で加工したりというように、1行1行の処理を実装すればよい。 この File.ReadLines() メソッドは、対象のファイルすべてをメモリに読み込むのではない。 1行ずつ読み取っては結果の文字列をその都度返すので、未使用となった文字列は適宜ガベージコレクターに回収されるに任せられる。 その結果、このメソッドは省メモリで動作し、大量の行から成る巨大なテキストファイルも一定の消費メモリだけで安定して処理が行える。 列挙を途中で抜けたらどうなるの?そんな便利な File.ReadLines() であるが、あるとき、ちょっと困ったことになった。 あんまり遭遇することはないシチュエーションと思われるのだが、File.ReadLines() で行読み取りを開始したあと、最終行まで列挙せずに、途中で列挙を中断する要件に遭遇したのだ。 中断する実装自体は別に問題ではない。 そうではなく心配なのは、列挙を中断したことで、File.ReadLines() が開いたテキストファイル読み取りの Stream が、開きっぱなしになってしまわないだろうか、という点だった。 もちろん、いずれガベージコレクターに回収されることで、最終的には Stream は閉じられる。 しかしそれでは、Stream が閉じられるタイミングに予測性がない。 今回自分が遭遇したシチュエーションでは、列挙中断後、即座に Stream が閉じられている必要があったのだ。 ふーむどうしたものかと思いつつ、ちょっと試しに下記のような実験コードを書いて試してみた。
すなわち、File.ReadLines() でテキストファイル中の行の列挙を開始し最初の1行をコンソールに表示するも、それだけで列挙を中断している。 で、その直後に当該ファイルの削除を試行している。 上記のように、foreach による列挙ループを break で中断したことによって、File.ReadLines() が開いた Stream が開きっぱなしになっているとしたら、続くファイル削除で IOException 例外が発生するはずだ。 実行したところ...で、実行したところ... なんと例外も発生せずに、正常にファイルの削除まで実行されたのだ。 自分の予測と違った振る舞いになったため、もう少し調べてみることに。 次は foreach で列挙ではなく、生の列挙子を自分で MoveNext() しながら while ループで列挙する実装を試してみた。
するとこちらの実験コードでは、期待どおり (?) に、ファイルの削除で IOException 例外が発生した。 例外メッセージはもちろん。他のプロセスで対象ファイルが使用中である、というものだ。 なにが起きているのかよくわからなくなってきたので、先の foreach 方式の実験コードをビルドした結果である .dll ファイルを ILSpy で見てみた。 すると、foreach って、using で囲ったコードに展開されているのであった...! (知らなかった...)
とはいえ、IDisposable ではなく IEnumerator<string> を using するとは何事だろうか。 さらに ILSpy でコードをたどってようやくわかった。 File.ReadLines() が返す列挙オブジェクト、及び、それが返す列挙子であるが、ちゃんと IDisposable インターフェースを実装していたのだ。 言い換えると、File.ReadLines() が返す列挙オブジェクト、及び、それが返す列挙子を IDisposable インターフェースでキャストすると、ちゃんと IDisposable インターフェースとしての参照が手に入るのであった。
それで列挙子を using 節で囲うことで、ちゃんと Stream を閉じる動作につながるのであった。 まとめこれまで自分はまったく理解していなかったが、列挙子に適宜 IDisposable インターフェースを実装しつつ IEnumerator<T> として公開するのは、定番のイディオムなのであろう。 なので、C# の foreach は、上記のとおり、using で列挙子を囲いつつ MoveNext() で列挙を進めるというコードに展開される仕様なのであろうと理解した。 たぶん、公式を含め、ちゃんとそこかしこのドキュメントに明記されていることなのだろうが、お恥ずかしい、今の今までこの foreach の仕組みをわかっていなかった。 [2019/11/08 追記] 公式ドキュメントについてお知らせ頂いた。 教えて頂いた公式ドキュメントへのリンクはこちら。 たしかに、Dispose する仕様について明記されている。 [― 追記ここまで ―] さておき、ということで、File.ReadLines() が返す列挙オブジェクトは、foreach で回したり、LINQ で使うぶんには、列挙が途中で中断しても、スコープを抜ける限りはちゃんと Dispose される、ということがわかった。 とはいえ、生の列挙子を自分で while ループで回すときなどは、もし列挙を中断する場合は自分で責任をもって Dispose する必要はある。 そんな実装が必要になるケースはかなり希だとも思うが...。 なお、MoveNext() が false を返すまですべて列挙し終えた場合は、自前で Dispose しなくても、列挙完了時点で Dispose されるので安心して大丈夫だ。 (File.ReadLines() が返す列挙オブジェクトがそのように実装されているため)。 #
by developer-adjust
| 2019-11-07 22:11
| .NET
|
Comments(0)
2019年 10月 29日
EntityFramework Core とはEntityFramework Core (以下 EFCore) とは、C# などの .NET 言語で使用される、データベースアクセス用途のライブラリだ。 この EFCore を使用して、C# などのプログラムからデータベースなどを読み書きする際は、生の SQL 文文字列を記述することは多くなく、大抵の場合はデータベースの要素にマッピングされるクラスやそのプロパティなどの操作を介して行なう (※開発チームの指針に依るかも知れません)。 とはいえ、アプリケーション実装者が書くコード上から生 SQL 文文字列が消えただけで、最終的にデータベースシステムに送り込まれるのは SQL 文である。 EFCore がその背後で SQL 文を生成してデータベースシステムとやりとりしてくれる訳だ。 EFCore は、プロジェクトの規模やチューニング要件次第ではあるものの、おそらくは多くの場面で便利に使える定番のライブラリであろう。 EFCore が吐く SQL 文を知りたいそんな便利な EFCore ではあるが、その EFCore が生成・発行する SQL 文文字列を把握したい状況は往々にしてある。 とくに、少々入り組んだクエリを実装したときに、果たして、ちゃんと効率の良い SQL を EFCore が生成できているか、確認したいときなどだ。 ASP.NET Core による Web アプリサーバー側実装において、EFCore が発行する SQL 文を知る方法については、過去に自分のブログ記事としても書いた。 いっぽうで、ASP.NET Core とかではなく、コンソールアプリなど、任意の形態のプログラムにおいて、EFCore が吐く SQL 文を把握したいケースもあろうかと思う。 そのような場合は、ASP.NET Core 上でのときとは違い、自分でロギング周りを配線してやる必要があるらしい。 ネットで検索してみると、以下のような記事が見つかった。 他にもいくつか類似の記事が見つかるが、いずれも、ロガープロバイダを自分でインスタンス化 (new) して差し込むこととなる。 EFCore の ver.3 では使えない!ところがである。 EFCore のバージョンが、ver.2.x までは上記記事の方法で目的を達成できるのだが、EFCore ver.3 になると、上記記事の実装ではコンパイルができなくなっていた。 例えば、デバッグ出力に SQL 文を出力するように実装してたコードを、EFCore v3 に昇格すると、以下のコンパイルエラーが発生する。 CS1729: 'DebugLoggerProvider' does not contain このエラーメッセージからも察しが付くと思うが、厳密に言うと、EFCore のバージョンというよりも、EFCore v3 が依存している、ロギング周りのパッケージの v3 が、旧バージョン用の上記実装に対して破壊的変更があったのである。 具体的には、旧バージョンでは、ロガープロバイダ (ConsoleLoggerProvider や DebugLoggerProvider など) のコンストラクタには、ログの分類 (カテゴリ) やレベルに応じた絞り込みを行なうフィルタ関数を、そのコンストラクタ引数に受け取るオーバーロードバージョンが用意されていた。 それが ver.3 以降は、そのようなオーバーロードバージョンのコンストラクタが廃止されたのだ。 (ver.2.x 時代から、すでに Obsolete 属性でマーク、すなわち廃止予定であるとちゃんと示されていたようではある。) EFCore ver.3 からは LoggerFactory.Create が便利ではどうするのかというと、LoggerFactory.Create() を使ってロガーを構成するやり方が使えるらしい。 なお、下記公式ドキュメントを見ると、ロガーの構成方法はそれはもう色々ありすぎて習得するのも骨が折れる印象だ。 さておき、詳細は公式ドキュメントに譲るとして、ざっくり下記のようなコードにて、デバッグ出力に EFCore が発行する SQL 文が出力されるようになった。
まとめ他にも LoggerFilterOptions オブジェクトを使ってログフィルターを構成するとか、上記実装例にしても、AddFilter() 拡張メソッドには少なくとも 9 種のオーバーロードバージョンがあったりとか、様々なやり方があるようだ。 とりあえず自分が検証した範囲では、上記実装例が、この用途ではいちばんシンプルに書けたので、ここに紹介する次第。 以上、EFCore v3 で、発行される SQL 文をログ出力する方法であった。 #
by developer-adjust
| 2019-10-29 21:51
| .NET
|
Comments(0)
2019年 09月 27日
.NET Core 3.0 の単一実行可能ファイル生成先日、公式リリースとなった .NET Core 3.0。 その新機能のひとつとして「単一ファイルの実行可能ファイル」を発行する機能がある。 これまでは、C# や VB、F# など .NET Core プラットフォームのプログラムをビルド・発行した場合、その最終成果物は、各種 .dll ファイル群がごちゃっと発行先フォルダに出力されるだけであった。 それが、.NET Core 3.0 の新機能「単一実行可能ファイル生成」を用いると、発行先フォルダには、たったひとつだけの実行可能ファイルが生成されるだけとなり、配布などの取り扱いが容易になる。 (なお、本機能の動作原理は、たとえて言うならば tar のような、どうやらある種の "アーカイブ" として必要 .dll ファイル群を単一実行可能ファイル内に収録しているようである) もっとも、これまでも ILMerge や ILRepack などなど、.NET プラットフォームのバイナリを単一ファイルにまとめる手法やツールは存在していた。 とはいえ、.NET Core SDK 本体に類似の機能が標準で搭載されており、すぐに使える点は大変便利である。 ということで、Windows OS 上で Visual Studio 2019 を使った開発体制にて、「単一実行可能ファイル生成」を実際に試してみた。 まずは動作を確認まずは試しにということで、Visual Studio 2019 のプロジェクトテンプレートから .NET Core コンソールアプリケーション (今回は言語は C#) を生成。 "Hello World" を表示するだけの C# プログラムだ。 そして下記記事を参考に、.csproj ファイルに2行書き加えてみる。
まずはここまででビルドして実行してみる。まぁ、普通に成功することを確認。 続けて、いよいよ発行である。 Visual Studio 2019 上の開発作業ということで、Visual Studio 上でフォルダへの発行プロファイルを作成し(下図)、発行を実行。 ![]() すると、手順どおりにやっているのだから当たり前であるが、無事、発行先には .exe ファイルがひとつだけ生成された。(実行環境に 64bit Windows10 OS を指定しているので、生成される実行可能ファイルは拡張子 .exe の PE ファイルである。) なお、たかが "Hello World" を表示するだけのコンソールアプリケーションであるが、その実行可能ファイルのサイズは 65.8MB になってしまった。 まぁ、ターゲットランタイムがポータブルでない、且つ、配置モードが自己完結 (Self-Contained) な発行成果物を、非圧縮でアーカイブしているだけのようなものなので、こんなものだろう。 他の OS 用にも発行できるなお、.NET Core といえばクロスプラットフォームである。 ということで、上記プロジェクトを、例えば 64bit Linux 用に単一実行可能ファイルを発行したい場合はどうするか? これはもう、何も難しいことはない。 ただただ単純に、ターゲットランタイムとして "linux-64" を指定した発行プロファイルを作成して、その発行プロファイルで発行すれば良いだけである (下図)。 ![]() だがしかし、そのプロジェクト、Windows 専用になってない?さてところで、上記までの手順だと、プロジェクトファイル内にターゲットランタイム指定が記載されているため、発行ではなくビルドの時点でも、64bit Windows10 用のバイナリを吐くように規定されてしまっている。 このようなプロジェクトファイルを、Linux や macOS など他の OS 上でビルドするとどうなるか? まぁ、実はビルドは成功する。 だが、さすがに実行まではできない。 (※実は、Windows10 上の Windows Subsystem for Linux, いわゆる WSL の中だと実行できてしまったりもするのだが、それはまた別の話。) かといって、ターゲットランタイムの指定をプロジェクトファイル内から記述削除し、ポータブル形式でてビルドしようとしても、今度はビルドが下記エラーで失敗するようになってしまう。
".NET Core といえばクロスプラットフォームである" といっておきながら、これはいただけない。 どうしたものか? 発行プロファイルで指定する答えは 「単一実行可能ファイル生成の指定は、プロジェクトファイル (.csproj) に書かない。代わりに発行プロファイル (.pubxml) に書く。」 である。 Visual Studio で開発していての発行プロファイルとは、その実態は、Properties フォルダ以下に配置された拡張子 .pubxml のファイルであり (下図)、プロジェクトファイル (.csproj) に対する差分である。 ![]() ということで、まずは .csproj ファイルからは、先ほど追加した単一実行可能ファイル生成およびターゲットランタイム指定の2行を削除して、元に戻す。
次に、先に作成していた発行プロファイルを (Visual Studio の発行プロファイル編集 GUI 経由ではなく) エディタで開いて直接編集し、"<PublishSingleFile>true</PublishSingleFile>" の行を追加する。 (※ターゲットランタイム指定 ("<RuntimeIdentifier>~</RuntimeIdentifier>") は既に含まれているはず)
以上で OK だ。 これでこのプロジェクトは、どの OS 上であっても、ターゲットランタイム = ポータブルとしてビルド・デバッグ実行可能となる。 その上で、上記発行プロファイルを使って発行すれば、64bit Windows 10 上で直接実行可能な (.NET Core Runtime の事前インストールも不要な) 単一ファイルの実行可能ファイルが手に入る。 もちろん、他の OS 用に発行プロファイルを変更したり発行プロファイルを追加したりしてもよい。 補足1 - dotnet CLI での開発時冒頭で示した公式ドキュメントを見ると、dotnet CLI を使う場合は、dotnet CLI 実行時のコマンドライン引数に、ターゲットランタイム指定と、単一実行可能ファイル生成指定を行なう方法が説明されている (下記例)。
このように、やはりプロジェクトファイル内では単一実行可能ファイル生成の指定はせず、発行時に個別に単一実行可能ファイル生成の指定を行なう (この例で言えば、dotnet CLI のコマンドライン引数で指定する) のがよさそうである。 また、dotnet CLI を使った場合でも下記要領にて、今回説明したような発行プロファイルを用いた発行をすることもできる。
補足2 - Visual Studio 2019 の発行プロファイル編集 GUI がおかしいここまでわざと気づかぬフリをしてきたが、実はここまで説明してきた手順だと、本記事のスクリーンショットにもちょっと見えているが、Visual Studio 2019 の発行プロファイル編集 GUI の表示上は、配置モードが「フレームワーク依存」と表示されてしまっている。 しかし実際に発行される内容は、配置モード = 「自己完結」 (Self-Contained) な内容となっている。 実はこれ、ターゲットランタイム指定 (RuntimeIdentifier) が指定されていると、配置モード指定が明示的に設定されていない場合、どうやらビルドスクリプト上は配置モードのデフォルト値が、「フレームワーク依存」から「自己完結」に切り替わっているようなのである。 ただ、そのビルドスクリプトの仕様が Visual Studio 2019 の発行プロファイル編集 GUI には反映されていないようで、すなわち、Visual Studio 2019 の発行プロファイル編集 GUI では、配置モードの指定が明示的に設定されていない場合は「フレームワーク依存」と表示されているようだ。 ぐだぐだ書いたが、このような混乱を避けるには、配置モード指定を発行プロファイル内に明記してしまえばよい。
あるいは Visual Studio 2019 の発行プロファイル編集 GUI 上で、配置モードを明示的に「自己完結」に選択し直しておいても OK だ。 ![]() このように常に配置モードの指定を明示的に設定しておくようにすれば、要らぬ混乱を避けられるであろう。 なお、この逆に、配置モードを「フレームワーク依存」として明示的に設定して単一実行可能ファイル生成指定で発行したらどうなるか? そうすると、OS に別途 .NET Core ランタイムの事前インストールが必要となるが、サイズは極小となる、そのような単一実行可能ファイルが発行先に生成される。 .NET Core SDK がインストール済みであることが前提な開発者向けのツールなど、これはこれで需要あるかもしれない。 まとめ単一実行可能ファイル生成は、その機能の性質上、ターゲットランタイムの指定が必須である。 そのため、単一実行可能ファイル生成の指定は、プロジェクトファイルに記入するのではなく、発行プロファイルに記入しておくのが、自分的にはお勧めである。 #
by developer-adjust
| 2019-09-27 18:56
| .NET
|
Comments(0)
2019年 08月 29日
背景 - EFCore が出力する SQL 文を知りたいデータベースを読み書きする ASP.NET Core Web アプリ実装において、データベースアクセスライブラリの選択肢としてとりあえずオーソドックスな "Entity Framework Core" (以下 EFCore)。 EFCore およびその前身である "Entity Framework" の面白いところは、データベースクエリの結果をオブジェクトに "マップ" する O/R マッパーというだけでなく、C# コードとして記述されたラムダ式木に基づく SQL クエリビルダーでもあるという側面がある。 さてそんな EFCore であるが、だがしかし、期待していたのとは異なる SQL が発行される可能性も少なくない。 とくに EFCore の ver.2.x だと、GROUP JOIN ですら、簡素な SQL を発行して早々にデータベースから多量のレコードをオブジェクトとして読み込んでしまい、オンメモリで残りの処理を実行することもあるという。 なお、本ブログ執筆時点では未だリリースされていない、次バージョン EFCore 3.x では、このあたりの挙動が変更され、SQL に変換できないクエリは例外を発生させることになるらしい。 この辺りの話題は、下記などが参考になる。 そんなこともあり、とくにアプリ開発中は、EFCore が発行する SQL 文を確認したいところである。 なお、コンソールアプリなどで EFCore 使用時に、その発行する SQL を確認するには、その目的のロギング機構を組み立てて適用すればよい。 詳しくは下記などが参考になる。 実は開発時ログ出力には、既に含まれているさてさて、ASP.NET Core アプリ開発中は、一般的なプロジェクトテンプレートから起こしたプロジェクトであれば特に、すでにロギング機構が組み込まれており、且つ、開発時向けのログレベル構成が適用済みである。 下記は開発時用構成ファイルである appSettigs.Development.json 中のログレベル構成だ。
この構成だと、Visual Studio で開発中であれば、Visual Studio の出力ウィンドウに色々ログが流れているのが確認できる。 (※デバッガなし実行でも表示される) また、dotnet CLI を使ってる場合も "dotnet run" などで実行すると、コンソールにログが流れるのを見ることができる。 実は既に、EFCore が出力する SQL 文は、このログの中に表示されているのだ。 よーく見ると、EFCore が出力する SQL 文が、ログの中に表示されているのを見つけることができる (下図、赤丸のところ)。 ![]() 大量のログ出力に埋もれてしまう...しかしである。 上図でもわかるように、このままでは、ブラウザ等からの要求ごとの ASP.NET Core 関連ログが大量に発生し、EFCore の SQL 文ログがそれらログに埋もれてしまう。 (ないしは、あっという間にスクロールアウトしてしまう) 実際問題として、この構成のままでは、大量のログの中から EFCore が出力する SQL 文を探し出して確認するという作業はあまり現実的ではない。 これは個人的な意見であるが、実のところ、ASP.NET Core 関連の要求処理時の情報ログは、あまり必要を感じない。 どのようなリクエストがブラウザから発生しているかを見るときは、どちらかといえば、ブラウザの開発者ツールで確認するだけで用が足りることがほとんどだ。 EFCore 以外のログレベルを調整ということで、appSettings.Development.json を改訂し、"Microsoft" カテゴリのログレベルを "Information (情報)" から "Warning (警告)" レベルに引き上げてしまうと良かろう。 そうすることで、ASP.NET Core 関連の大量の情報ログが表示されなくなる。 ただしそれだけだと、EFCore の SQL 文出力も同じく表示されなくなってしまう。 そこで、EFCore の SQL 出力だけはログ出力されるよう、"Microsoft.EntityFrameworkCore.Database.Command" カテゴリのログレベルを、再び "Information" としてオーバーライドするとよい。 最終的な appSettigs.Development.json は下記のような感じになる。
これで下図のような感じで、EFCore の出力する SQL 文だけが都度流れるようになる。 ![]() 自分の開発スタイルとしてはこれくらいのログレベルでちょうどいい感じだ。 以上、すべてのケースでこのログレベル構成が最適とは言わないが、同じような境遇の人にこの情報が役に立てば幸いである。 補足データベースアクセスのためには、EFCore 以外にも選択肢はいろいろあるので、発行される SQL を完全に制御したい場合などは、無理に EFCore を使う必要もない。 下記など参考にされたし。 #
by developer-adjust
| 2019-08-29 21:11
| .NET
|
Comments(0)
2019年 07月 06日
C# による ASP.NET Core プログラミングにおける、単体テストを実装していての話題。 ネットで検索すればすぐに見つけられるような内容かとも思うが、自分の為の備忘録を兼ねてここに記す。 さて、前述のとおりの単体テストを実装している際、IOption<T> をコンストラクタ引数に求めるオブジェクトをテスト対象とすることがある。 さらにたまーにではあるが、コンストラクタ引数に IServiceProvider を直に要求するオブジェクトを扱うこともある。 このようなケースで、どのように IOption<T> ならびに IServiceProvider をテストプログラム中でインスタンス化すればよいか、という話である。 IOption<T>ASP.NET Core におけるオプション構成は、通常は Startup クラス中の ConfigureServices メソッド内で初期化するのが通常だ (下記は MyOptions 型のオプションを appSettings.json などの構成情報から読み取って取得する例)。
こうしておくと、MVC/API コントローラなどで MyOptions 型のオプション情報が必要な場合は、そのコンストラクタ引数として IOptions<MyOptions> 型の引数を用意しておけば、依存性注入機構によって引き渡されるようになっている (下記例)。
さてこのような IOption<T> を自前のテストコード中で直に生成するには、Microsoft.Extensions.Options 名前空間の Options クラスの Create<T>(T options) 静的メソッドを用いる。 この Create<T>(T options) 静的メソッドの引数に、任意の型 T のオブジェクトを渡せば、その戻り値で IOption<T> が返る (下記例)。
IServiceProviderIServiceProvider を自前のテストコード中で生成するには、Microsoft.Extensions.DependencyInjection 名前空間の、ServiceCollection クラスを使う。 手順としては、まずは ServiceCollection クラスをインスタンス化する。 次にこの ServiceCollection オブジェクトに対し、AddSingleton<T1,T2>() などをはじめとしたおなじみのメソッドを呼び出して、そのテストで必要となるサービスを登録しておく。 (すなわち、IServiceProvider を要求しているテスト対象のコード中では、その IServiceProvider に対して GetService<T1>() とか呼び出して、サービスを入手しているであろうため) 以上の準備が整ったら、ServiceCollection オブジェクトの BuildServiceProvider() メソッドを呼び出す。 そうすると、BuildServiceProvider() メソッドの戻り値として、IServiceProvider が返る。
使い終わったら、Dispose() メソッドを呼び出してリソース破棄しておくことを忘れずに。 以上!
#
by developer-adjust
| 2019-07-06 23:02
| .NET
|
Comments(0)
2019年 06月 09日
Windows OS 上で C# による Azure Functions 開発を行なおうとしての話。 とある事情でまっさらの環境に Visual Studio 2019 をインストールするところから開始。 Visual Studio 2019 のインストーラにお任せで、Azure Storage Emulator (ver.5.9) や SQL Server 2017 Express および LocalDB も自動でインストールされるに任せた。 ということで早速にプロジェクトテンプレートからちゃっちゃっと C# で Azure Function プロジェクトを作り、Ctrl + F5 でビルド & 実行。 Azure Functions は背後で Azure Storage を使っている関係で、開発環境におけるローカル実行時は、Azure Storage Emulator がローカル環境で起動される。 ...が、なんとここで想定外に、Azure Storage Emulator がエラーを吐いて起動に失敗してしまった。 エラーの原因は「データベースを作れません」!?コンソールには以下のとおり出力されていた。
Azure Storage Emulator がその永続化層に使用するために、SQL Server LocalDB に "AzureStorageEmulatorDb59" という名前のデータベースを作ろうとするも、何やら上手くいっていないようである。 とりあえずネットで検索してみると、StackOverflow に同様のトラブルでの質問投稿を発見した。 今までこんなトラブルに遭遇したこともなくて、Azure Storage Emulator を使い倒したこともなく知らなかったのだが、上記 StackOverflow のスレッドを見ると、Azure Storage Emulator には様々なコマンドライン引数指定ができるとのこと。 例えば、"/forceCreate" スイッチ付で、"init" を指定することで、データベースが壊れた場合でも再作成・最初期化できるとのこと。
まずは自分のこのケースでも上記コマンド実行を試してみた。 しかし残念ながら、自分のケースでは表示されるエラーメッセージは変らず。 ちゃんとエラーログを読もうさらに先の StackOverflow をスレッドを読み進めるに、まぁ、ちゃんとログ読もうぜ、という話らしくて、改めてちゃんと、Azure Storage Emulator のエラーログファイルを見てみることにした。 ちなみに Azure Storage Emulator のエラーログファイルは、下記フォルダに「error.log」というファイル名で保存されている。
error.log ファイルの内容を "AzureStorageEmulatorDb59.mdf" で検索してみたところ、下記行を発見。
よーく見ると、作成しようとしているデータベースの物理パスがなんかおかしい。
となっているが、あれ、ユーザーフォルダ名 (C:\Users\jsakamoto) とファイル名 (AzureStorageEmulatorDb59.mdf) との間のディレクトリ区切りが無くなってない? 正しくは
じゃないですか? どうしてこうなった? 原因は SQL Server 側の不具合?先の StackOverflow のスレッドを見直していると、コメント欄に答えがあった。 Indeed this was the issue. I installed CU13 HotFix for SQL Server 2017 and it works now. – Andrii Mar 8 at 21:07 どうやら CU13 という SQL Server 用の Hotfix で本現象は解消できるらしい。 つまり SQL Server LocalDB 側の不具合だったということだろうか。 ということで上記コメントのリンクをたどりながら下記から CU13 Hotfix をダウンロードして適用すれば直るらしい。 他の回避策ただちょっと今回は事情があって、冒頭で "とある事情でまっさらな環境に..." とか書いたように、すぐにダウンロード & インストールできない、特殊な状況であった。 自分がこれまで今回のようなトラブルに遭遇しなかったのも、おそらくはインストールしたての環境ではなくて、ちゃんと最新の Update を適用済みの環境だったからかもしれない。 さておき、今の状況ではこれは詰んだかなー、と諦めようかと思っていたところ、別の回避策を発見。 先にも書いたが、Azure Storage Emulator にはいろいろなコマンドライン引数指定ができるのだが、その中に、永続化層に使用する SQL Server インスタンスを指定する方法があるとのこと。 具体的には、"/server" スイッチに続けて、使用する SQL Server インスタンスを指定して、"init" 動作を指定するらしい。 試しにと言うことで、この環境にインストール済みである、SQL Server 2017 Express のインスタンス (インスタンス名は "SQLEXPRESS") を使用するように、Azure Storage Emulator を構成・初期化してみることにした。
すると、CU13 Hotfix が未適用でも、ちゃんと Azure Storage Emulator 用に、データベース AzureStorageEmulatorDb59 が SQL Server 2017 Express 上に作成された。
もちろんその後の Azure Functions プログラムのローカル実行も難なく実行されるようになった。 もちろん CU13 Hotfix も有効!後日、CU13 Hotfix を適用後に、改めて "AzureStorageEmulator.exe init" を実行したところ、こちらも無事成功し、SQL Server LoacDB 上でも正常稼働するようになった。 まとめということで、 Azure Storage Emulator のコマンドライン引数指定、"init" コマンド実行に加えて付属のスイッチ群 "/forceCreate" "/server" などについて知っていると、何かトラブルの際に役立つかも知れない。 以上。 #
by developer-adjust
| 2019-06-09 13:15
| その他IT系
|
Comments(0)
2019年 05月 24日
Windows OS 上で SPA を開発していてのお話。 先日、しばらく前に構築した、とある Web アプリ (SPA) プロジェクトを保守する必要が出てきた。 久しぶりに当該プロジェクトをリモートリポジトリからローカル開発環境に git clone し、とりあえず npm install を実行したところ、いきなりエラーになってしまった。
どうやら、SaSS を CSS へ変換する Node.js 用ライブラリ node-sass のパッケージインストールでこけたっぽい。 使用している node-sass のバージョンは v.4.7.2。 開発当時はちゃんとクリーンビルド成功してたのに、なんで? Node.js のバージョンが当時と今とで違う当該プロジェクトの開発当時と、今回とで環境で変っていたのは Node.js のバージョンだった。 開発当時、インストールされていたのは、たしか Node.js の v.8 系。 現在は Node.js を v.10.15.3 にアップデートしたあとの環境である。 どうも、この Node.js のバージョン違いが、開発当時は問題なくて、現在だと npm install できなくなる原因らしい。 さて、この npm install できなくなる問題をどうにかしないと、本題の保守作業に入ることもままならない。 どうしたものか。 Python や C/C++ の環境が必要?インターネット検索した感じだと、どうやら、node-sass のバイナリビルドのために、Python 2.x や C/C++ の環境が必要らしい。 で、macOS や Linux 系列だと、だいたい Python や gcc 入ってたりするので、すんなり node-sass のバイナリビルドが済んでしまう模様 (あくまでもネットで聞きかじった情報であるが)。 ところが Windows OS ではそのあたりでひっかかって、node-sass のバイナリビルドができずに npm install がコケて終わり、となるらしい。 たしかに自分の場合も、それっぽいログが表示されていた。
このような場合の定番の解決方法は、「npm install --global windows-build-tools」を実行して、Windows OS 上でもそれらビルドに必要な環境を npm でインストールしてしまえばよいようだ。 でもちょっと待て欲しい。 開発当時は、そのようなビルドツールが未インストールでも、すんなり npm install できていたのだ。 なんでだろう? ビルド済みバイナリが GitHub 上にある!いろいろ調べていくうちにだんだんとわかってきた。 まず、node-sass の Node パッケージスクリプトは、常にはバイナリビルドを実行しようとはしない。 まずは node-sass の GitHub リポジトリに登録されている、ビルド済みバイナリのダウンロードを試みるようだ。 そして、ダウンロードしようとしている node-sass のビルド済みバイナリのファイル名の末尾には、対応している Node.js 実行環境の "モジュールバージョン" が含まれていた。 具体的には、Windows OS 用バイナリの場合、 「win32-x64-{ここにモジュールバージョン}_binding.node」 というファイル名になる。 node-sass の GitHub リポジトリで調べてみると、node-sass v.4.7.2 のビルド済みバイナリは、モジュールバージョン 57 まで登録がある。 しかし今回の開発環境 Node.js v.10.15.3 のモジュールバージョンは 64 であった。 ![]() ということで、node-sass の GitHub リポジトリには登録のない、"v.4.7.2 で、且つモジュールバージョンが 64" であるビルド済みバイナリのダウンロードを試みて失敗 (HTTP 404 Not Found) となっていたようだ。 (下記はその証拠の npm install ログ表示。)
なお、node-sass の Node パッケージスクリプトは次なる手段としてローカル環境でのバイナリビルドを開始する。 だがしかし自分の環境ではビルド用のツールの整備がなっていないので、これもコケてしまい結局エラーで終了、ということだったらしい。 さて、改めて node-sass の GitHub リポジトリの Release ページを見てみると、ちゃんとビルド済みバイナリが用意済みの、対応されている Node.js バージョンが明記されている。 ![]() これを頼りに今回は node-sass v.4.9.0 以降を使用するよう packages.json を更新した。 その結果、自分のローカル環境にビルドツール類をインストールすることなく、無事 npm install が成功するようになった。 まとめということで、Windows OS 上で、必ずしも windows-build-tools Node パッケージの利用をはじめビルドツールの整備をしなくても、node-sass のバージョンを適切にアップデートしていけば、すんなり npm install を通すことはできる、という話。 なお、Node.js のいつのバージョンからかよく覚えていないが、最近の Node.js Windows 版のインストーラーでは、ビルドツールも一緒にインストールするかどうかの選択肢が出てきてるっぽい。 ![]() この選択肢を有効にして Node.js をインストールしてある環境ならば、このような node-sass を npm install できない問題は起きないのだろうか。 検証したことはないが、気になるところである。
#
by developer-adjust
| 2019-05-24 19:43
| Web系一般
|
Comments(0)
2019年 04月 23日
".NET HTTP REPL" とは?.NET HTTP REPL とは、.NET Core 上で動作するコマンドラインツール。 既存のコマンドラインツールで類似なものを挙げるとすれば、curl や wget が近いだろうか。 すなわち、コマンドプロンプト (ターミナル) 上から指定した HTTP 要求を送信し、返信されてきた応答を表示する類いの CUI (コマンドライン ユーザー インターフェース) プログラムである。 しかしながら、このツールの名前に "REPL" と付いているように、基本的に対話形式で使用する形態となっているのが .NET HTP REPL の特徴である。 REPL (Read-Eval-Print Loop) とは、入力・評価・出力のループのこと。主にインタプリタ言語において、ユーザーとインタプリタが対話的にコードを実行する。 ちなみに、.NET HTTP REPL のソースコードは GitHub 上の AspLabs というリポジトリでホスティングされており、Apache 2.0 ライセンスで公開されている。 このリポジトリの README には、"Repo for ASP.NET experiments that are not ready for a production release." と書かれており、この先どうなるか、心配すべきかわくわくすべきか迷うようなプロダクトが詰め込まれているように感じた。 ということで、実は .NET HTTP REPL は本記事を投稿の時点では公式リリース版ではなくプレビュー扱いである。 適宜注意されたし。 .NET HTTP REPL のインストールさて、.NET HTTP REPL を使用するには、まずは .NET Core SDK のインストールが必要である。 .NET Core SDK がインストールされ、コマンドプロンプト (ターミナル) 上から dotnet コマンドが実行可能になっていれば、.NET HTTP REPL のインストールは以下のコマンドを実行すればよい。 > dotnet tool install --global dotnet-httprepl --version 0.1.0-preview.19119.4 このようにすることで、dotent Global Tools として nuget.org 経由で .NET HTTP REPL 本体がダウンロード、インストールされる。 これで .NET HTTP REPL が使えるようになる。 .NET HTTP REPL の起動・使い方.NET HTTP REPL は dotnet コマンドのサブコマンドという形態で実装されている。 そのため、コマンドプロンプト (ターミナル) から以下のコマンドを実行することで起動され、.NET HTTP REPL の対話モードに入る。 > dotnet httprepl 最初に書いたとおり、.NET HTTP REPL は対話形式で使用する、HTTP 要求を送信するツールである。 まずは送信先 HTTP サーバーのホスト、プロトコル、ポートを、"base" という HTTP 要求送信の基点として設定しておく必要がある。 これは、.NET HTTP REPL の対話モードのコンソールにて、以下のように "set base" コマンドで設定する。 ~ set base http://localhost:50893/ base 設定が完了すると、.NET HTTP REPL のプロンプトも "(Disconnected)~" から "http://localhost:50893/~" というように設定された base に変る。 ![]() こうして base 設定が済んだら、あとは、HTTP 動詞と同じ名前で用意されているコマンドを入力すればよい。 例えば、URL = api/SampleData/WeatherForecasts に HTTP GET 要求を送信する場合は下記のような感じで、割と直感的に使える。 ~ GET api/SampleData/WeatherForecasts HTTP サーバーからの返信結果は、例えば JSON 形式で返された場合はよしなに色づけ・書式化されて表示される。 ![]() 当然 POST をはじめ、PUT, DELETE などの HTTP 動詞を送信するも可能で、また、POST や PUT で JSON コンテンツを送信したい場合は、-c スイッチに続けて送信したいコンテンツを記述すればよい。(以下は「ちょまど問題サーバー」に回答を送信して正答数を得る例) POST https://chomado-problem-server.azurewebsites.net/answer -c [1,2,4,4,1,2,3,4,1,2] 対話モードを抜けて .NET HTTP REPL を終了するには exit コマンドを実行すればよい。 ~ exit .NET HTTP REPL には cd コマンドがあるさて .NET HTTP REPL のおもしろいところとして、対話形式のツールであることから状態が持てるので、"cd コマンド" が用意されていることが挙げられる。 つまり、HTTP 要求送信先 URL をスラッシュ区切りとしてディレクトリに見立てて、"現在ディレクトリ" を移動できる、という仕組みである。 例えば以下のように api/SampleData "ディレクトリ" まで、cd コマンドで "降りて" いれば、 ~ cd api/SampleData GET コマンドで api/SampleData/WeatherForecasts に要求送信するには、前半の api/SampleData を省略した、WeatherForecasts だけを送信先 URL として記述すればよい。 /api/SampleData~ GET WeatherForecasts "現在ディレクトリ" は .NET HTTP REPL のプロンプトに表示されているほか、引数なしの cd コマンドを実行することでも表示される。 ![]() 特定のリソースを指す REST 風な Web API に対して読み書き操作を色々試したいときなど、cd コマンドで URL の共通部分まで、あらかじめ "降りて" おけば、入力の手間が省けて良さそうな感じである。 ※2019/04/22現在、cd コマンドが例外を発生させることがある (当方の環境。.NET Core 3.0 Preview 4)。但しとりあえず .NET HTTP REPL は動作継続し、cd コマンドによる "現在ディレクトリ" の変更は行なわれているようである。 Swagger にも対応!?.NET HTTP REPL の対話モードで help コマンドを入力すると、どんなコマンドがあるのかヘルプ表示される。 ![]() これを見ると、まだ試せていないのだが、"set swagger" というコマンドもあるようで、どうやら Swagger 形式の Web API 定義 JSON を読み込んで便利に使えるようだ。 また、run コマンドによって "スクリプト" を実行できる、ともある。 これもまだ試せていないので、具体的にどのように使えるものなのか現時点ではわかっていないのだが、興味を引かれるところである。 感想.NET HTP REPL は、REPL と名前についているとおり対話形式で使用するものであり、起動時のコマンドライン引数で何か実行するということはどうやらできない模様だ(もしかしたら、スクリプト機能などで可能なのかもしれないが、まだよくわかっていない)。 そういう用途では、やはり curl などのほうが使えるということになりそうだ。 また、最近の Windows 10 OS には curl コマンドも標準で入るようになったので、自分がわかっている範囲だけでも、macOS/Linux各種ディストリ/Windows などなど、幅広い OS で使えるのも curl の良いところだろう。 他方、.NET HTTP REPL であるが、サーバーから返信された JSON コンテンツを、インデント付きで書式化・色づけして表示してくれるので、サーバー応答の JSON が読みやすいと感じた。 しかしながら、curl コマンドと jq コマンドの組み合わせ技のように、サーバー応答の JSON をフィルタしたり射影したりなどの凝った加工は、.NET HTTP REPL にはできないように見受けられる。 個人的には .NET HTTP REPL が直感的に使えるところが気に入った。 "GET /api/..." のように、自分が Web ブラウザになった気分で HTTP 要求を発行できるのが心地よい。 また、cd コマンドによる "現在ディレクトリ" の概念もおもしろいところだと思う。 さらに今後、Swagger 対応の機能の使い方がわかれば、もっと強力に使えるツールとなるかもしれないと期待もしている。 各種自動化やシェルスクリプトの記述などにおいては、相変わらず curl の出番となると思う。 そのいっぽうで、Web アプリ開発中に臨時的に Web API 呼び出しを試してみたいときなど、対話形式での使用のほうがよいケースでは、積極的に .NET HTTP REPL を使いそうな気がしている。 PowerShell の Invoke-WebRequest や Invoke-RestMethod は... うーん、自分が書く自動化系のスクリプトは PowerShell が俄然多いのでそういう PowerShell スクリプトファイル内ではよく登場するものの、今回の .NET HTTP REPL 的な、臨時的・対話的用途には、自分の手にはなじまなかったです、はい。 #
by developer-adjust
| 2019-04-23 00:19
| Web系一般
|
Comments(0)
2019年 03月 31日
Excel ファイル (.xlsx) を、C# プログラムから読み書きするときの話。
C# プログラム (というか、.NET 系言語) から Excel ファイル (.xlsx) を読み書きするためのライブラリとして、自分はこれまで、ClosedXML を愛用してきた。 ClosedXML を使用してきた格別の理由があったわけではないが、大して公式ドキュメントに目をとおさずとも、自分の予想どおりな感じで使える API 体系がなじんだのかなと思う。 ClosedXML の限界(?) に遭遇!さてそんな ClosedXML だが、自分のとある要件の用途で、利用に堪えない制限があることが、最近になってわかった。 Excel ファイル中のシートをコピー (複製) したときに、シェイプ (図形) がコピー先に複製されないのだ。 具体的にはこんな感じのコードで、シート「Sheet1」を、シート名「Sheet2」として複製したとする。
そうしてできた Excel ファイルを MS Excel などで開いてみると、「Sheet1」にはあった図形 (シェイプ) が、「Sheet2」には複製されていないのだ (画像は複製されている)。 ![]() ここでの詳細は割愛するが、ClosedXML におけるシート複製のソースコードに目をとおした感じだと、シート中に含まれる様々なオブジェクトを列挙しつつ複製先のシートにそれらオブジェクトも複製していく実装になってるっぽいのだが、その複製処理のなかに、そもそもシェイプは対象として含まれていないように読めた。 代替案 - EPPlusさてどうしたものかな、と思案するに、.NET プログラミングにおける Excel ファイルの読み書きライブラリとして著名なもうひとつのライブラリ、EPPlus の存在を思い出した。 EPPlus を使った場合の Excel シートを複製する C# プログラムは、以下のような感じとなる。
EPPlus を使った上記コード例であれば、期待どおり、「Sheet1」に含まれる図形 (シェイプ) も、複製先のシートに複製された。 このような顛末から、今回案件では ClosedXML から EPPlus への乗り換えを行なった。 以上の動作を確認できるソースコード一式を GitHub に掲載しておいた。 補足乗り換えではなくて ClosedXML に対し、図形 (シェイプ) も複製されるようにプログラム変更する貢献をすることも選択肢にはあり得た。 しかしながら今回は少々急いでいたこともあり、ClosedXML への貢献ではなく EPPlus への乗り換えで済ませる選択肢をとった。 ということで、また、さすがに同様の件は、既に誰かが Issue あげてて対応が将来版で計画されているのではないかという気もしてて、ClosedXML には Issue すら報告していない。 それはいわゆる OSS に対する "タダ乗り" ではないか、という後ろめたさもあるのだが、そうはいっても取り急ぎ目先の問題を解消しなくてはいけない場面もある訳で (今回がまさそうだ)、また、OSS 全般という見方をすれば自分なりに活動はしているつもりなので (今回は取り上げなかったが、かつては C# から Excel ファイルを読み書きするときの定番であったライブラリ「NPOI」には、少しばかりだが contribute していたこともあり)、そこは大目に見てもらいたいなと考えている次第である。 #
by developer-adjust
| 2019-03-31 22:44
| .NET
|
Comments(0)
|
ファン申請 |
||