Linuxでdotnetを扱う場合の GetCurrentDirectory の罠


GetCurrentDirectoryの設定の罠

どのASP.NET Coreや.NET CoreコンソールのドキュメントにもでてくるこのGetCurrentDirectoryメソッド。このように書かれているからこれを使う、という初心者的な考えで実装すると嵌まります。猫は、結構長いこと使ったりしてる言語/環境ではあるのですけど、やはり実行環境が変わる、実行したい方法が変わるとなると、意外にその概念を忘れます。

特に、Linux等でコンソールで動作させる場合、クローンを使ったりするときに、そのことを考えずに設定すると正常動作しない可能性があります(というか、これにかなり悩みました)。


config が読み込まれないじゃないか...

例えば、/etc/hogehoge/app/hoge.dll に、コンソールアプリ(バッチ処理)をおいて、定期的に実行しようとした場合、クローンに 0 0 * * * hoge /bin/dotnet /etc/hogehoge/app/hoge.dll として設定したとします。ちなみに、/etc/hogehoge/app/hoge.dll は、Directory.GetCurrentDirectory() メソッドを利用して、appsettings.json を読み込んでいます。すると、このプログラムのDirectory.GetCurrentDirectory()メソッドが返すカレントディレクトリは、どこになるのか?

そう、hoge のホームディレクトリになります。基本、hoge.dll と同じ場所に appsettings.json があると想定してデプロイすると、コンフィグを読めなくて死にます。


ASP.NET Core では、公式でもGetCurrentDirectoryを使う記述が書かれている

Microsoftのドキュメントにも、Directory.GetCurrentDirectory()メソッドを使って、コンフィグを取得する処理が書かれている。このままドキュメントの記述に習って、systemd などに書いた場合、気をつけないといけない点があります。WorkingDirectoryの設定が正確にされていないと、CurrentDirectory()メソッドは指定ユーザーのホームを返してくれるということです。そのため、dotnet run で実行しているときは正常に動いているにもかかわらず、デプロイしてsystemd なりで動作させようとすると、動かないという罠にはまり悩み続ける可能性があります。

そして、以外にこれらの内容を記した所はあまりないので、初心者には不親切だったりします。

https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2


元に戻って

ASP.NET Coreでsystemd を使う場合は、WorkingDirectory 項目にパスを指定すればいいのですが、では、Cronの場合はどうすればいいのでしょうか。


cd を使ってカレントの移動

0 0 * * * root cd /etc/hogehoge/app/;/bin/dotnet /etc/hogehoge/app/hoge.dll

今のところ、これぐらいしか対策はなさそうです。直接 dotnet を叩くのではなく、 .sh を通してもいいのですが、書く内容についてそれほど変わらないかと思います。

通常の.NET Framework の場合は、AppDomain.CurrentDomain.SetupInformation.ApplicationBase プロパティを使うことで、.exeや.dllのある場所のパスを取得可能ですが、残念ながら .net standard や .net core 2.2には追加されていないようで、利用できませんでした。そのため、デプロイ時点で、WorkingDirectory(CurrentDirectory)を意識して設計・設定しなと碌な目に遭いかねません(猫みたいに)ので注意しましょう。


AppDomain.CurrentDomain.BaseDirectory プロパティ

これで、アプリケーションのおいているディレクトリがわかる?というコメントをいただいているのですが、検証したところ、ちゃんと実行パスのディレクトリが返ってきました。

あとは、よく共有ライブラリ用DLLを作って、それをlib/hogehoge.dll として入れている。その共有ディレクトリないでもAppDomain.CurrentDomain.SetupInformationほげほげを使っていたことがあり、こちらはどこのパスが返ってくるのか確認するだけです。