写真 沖縄県宮古島市の来間島(くりまじま)でASP.NET MVCサイト作成中のノマドワーカー
ワテの場合、ノマドワーカーに憧れる。
ノマドワーカーとは英語で「遊牧民」を意味する「ノマド (nomad)」と、「働く人」を意味する「ワーカー (worker)」を組み合せた言葉である。(ノマドワーカーのwikipediaより引用)
その前に、まずはノートパソコンを入手する必要があるなあ。
ちなみに今年の8月はコロナウイルス騒動で不要不急外出を控えているワテであるが、近所の海水浴場へ一、二回は行きたいと思っている。
さて、ワテの場合、数年前にVisual Studioを使ってASP.NET MVCのWEBサイト作りに熱中していた時期がある。
Windows VPSレンタルサーバーを契約したので、Visual Studioを使ってWEBサイトの作り方を必死で独学で勉強したのだ。
その時に作ったサイトの一つが、「パソコン自作シミュレーター」のサイトだ↴
まあ、要するにアマゾンで人気のPCパーツを使ってパソコンを組み上げる場合の費用を計算できるサイトなのだ。
今思えばよくこんなややこしいサイトを作ったと思う。
ややこしいと言うのは、やっている事自体そんなにややこしくは無いのだが、ASP.NET MVCのWEBプログラミング初心者だったワテが、ネットのサンプルなど見よう見まねでサイトを作ったので、ソースコードがヘンテコでややこしくなったと言うのが正しい。
データベースはSQLサーバーExpressを使っているのだが、その当時はEntity FrameworkもCode Firstも知らなかったので、生SQL文を文字列で作成して実行すると言う、超ローレベルな手法で作っているのだ。
このプロジェクト作成に当たり、ASP.NET MVCが持つAreas(エリア)と言う手法を初めて使った。エリアの機能はなかなか便利だった。エリアの詳細は機会が有れば別の記事で紹介したい。
さて、久しぶりにこの「パソコン自作シミュレーター」サイトのプロジェクトをVisual Studio 2019で開いて、ローカルなIIS(つまりワテの開発PC)で実行してサイトを表示してみたところ、エラーで失敗した(下図)。
図 ASP.NET MVC実行時のアプリケーションでサーバー エラーの例
ローカルなIISでも、公開しているWindows VPSのIISでも、データベースは同じものを見ているので、正常動作した場合には上記URL https://www.wareko.net/WareAmaSim/pc/ と同じ画面(下図)が表示されるはずなのだが。
図 パソコン自作シミュレーターのサイト
当記事では、ASP.NET MVCのプロジェクトを数カ月ぶりくらいにVisual Studioで開いて実行した時に良く経験するこの手のエラーの解決方法を紹介したい。
では、本題に入ろう。
ASP.NET MVC実行時のアプリケーションでサーバー エラーの詳細
ワテが経験したエラーのメッセージは以下の通り。
‘/WareAmaSim’ アプリケーションでサーバー エラーが発生しました。
ファイルまたはアセンブリ ‘System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
説明: 現在の Web 要求を実行中に、ハンドルされていない例外が発生しました。エラーに関する詳細および例外の発生場所については、スタック トレースを参照してください。
例外の詳細: System.IO.FileNotFoundException: ファイルまたはアセンブリ ‘System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
ソース エラー:
現在の Web 要求の実行中にハンドルされていない例外が生成されました。障害の原因および発生場所に関する情報については、下の例外スタック トレースを使って確認できます。
スタック トレース:
[FileNotFoundException: ファイルまたはアセンブリ 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。] System.ModuleHandle.ResolveMethod(RuntimeModule module, Int32 methodToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount) +0 System.ModuleHandle.ResolveMethodHandleInternalCore(RuntimeModule module, Int32 methodToken, IntPtr[] typeInstantiationContext, Int32 typeInstCount, IntPtr[] methodInstantiationContext, Int32 methodInstCount) +115 System.ModuleHandle.ResolveMethodHandleInternal(RuntimeModule module, Int32 methodToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext) +66 System.Reflection.CustomAttributeData..ctor(RuntimeModule scope, CustomAttributeRecord caRecord) +64 System.Reflection.CustomAttributeData.GetCustomAttributes(RuntimeModule module, Int32 tkTarget) +93 System.Reflection.CustomAttributeData.GetCustomAttributesInternal(RuntimeAssembly target) +72 System.Reflection.RuntimeAssembly.GetCustomAttributesData() +10 Owin.Loader.DefaultLoader.SearchForStartupAttribute(String friendlyName, IList`1 errors, Boolean& conflict) +143 Owin.Loader.DefaultLoader.GetDefaultConfiguration(String friendlyName, IList`1 errors) +46 Owin.Loader.DefaultLoader.LoadImplementation(String startupName, IList`1 errorDetails) +75 Owin.Loader.DefaultLoader.Load(String startupName, IList`1 errorDetails) +21 Microsoft.Owin.Host.SystemWeb.OwinBuilder.GetAppStartup() +115 Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +28 System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +119 Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +106 System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +523 System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +176 System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +220 System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +303 [HttpException (0x80004005): ファイルまたはアセンブリ 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。] System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +657 System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +89 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +189バージョン情報: Microsoft .NET Framework バージョン:4.0.30319; ASP.NET バージョン:4.8.4084.0
「パソコン自作シミュレーター」のサイトをローカルIISで実行した時に出たエラー
まあ、行き成りこんな訳分からん画面が表示されると、本当に訳分からんw
ASP.NET MVCプログラミング初心者の頃は(今でも初心者だがw)、こんなエラーメッセージが出ても何が何だかさっぱり分からなかった。
その結果、訳分からないままNuget Package Managerを使ってインストール済のパッケージを最新版に更新してみたり、参照の追加機能の画面で、良く分からないまま参照設定を弄ってみたりした。
その結果、益々泥沼に嵌ってしまい、撃沈するのである。
あかんがなw
Web.configファイルを修正する
さて、その後、数年間に渡りWindows VPSを運用しながらASP.NET MVCや最近ではASP.NET MVC Coreのサイトを作成して来たので、今ではある程度はこの手のエラーに対する解決方法も分るようになった。
今回発生したエラーでは、そのエラーメッセージの最初の部分にヒントがある。
ファイルまたはアセンブリ ‘System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
こういう場合には、ワテの経験ではWeb.configファイルの中の記述を修正すれば治る場合が多い。
Web.configファイルとは何かは、ワテはまだ上手く説明出来ない。
ワテがWeb.configファイルに関して知っているのは、XML形式で記述するのと、そのWEBサイトに関する各種の設定を記述出来ると言う程度だ。
例えばパソコン自作シミュレーターのサイトを作った時には、SSO(Single Sign On)の設定をこのWeb.configファイルを使って実現出来た。
SSOとは、例えば
https://wareko.net
のドメインにユーザー登録やログイン機能を追加した場合、そのサイトで無事にユーザー登録してログインしたユーザーが、同一ドメイン内の他のサイト例えばパソコン自作シミュレーターのサイト
https://www.wareko.net/WareAmaSim/pc/
でもそのログイン状態を引き継ぐ仕組みだ(ワテの理解では)。
その結果、wareko.netドメイン内では一つのユーザーIDを作成するだけで、各種のサブサイトでもそのユーザーIDを使えるのだ。
Web.configファイルでアセンブリ System.Runtime 定義行を探す
さて、Web.configファイルを開いてキーワード “System.Runtime” で検索すると、下図右側の<dependentAssembly> 要素</dependentAssembly>で囲まれたやつがヒットする。
図 Web.configファイル内のアセンブリ System.Runtimeに関する行
その部分を以下に引用する。
<dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1.1" /> </dependentAssembly>
冒頭で示したエラーは、このdependentAssemblyの辺りを修正すれば治る可能性が高いのだが、具体的に言うとnewVersionの数字 “4.1.1.1” だ。
上図のVisual Studioの画面キャプチャの左側ペインに表示されているSystem.Runtimeの右クリックメニューを表示すると以下の通り。
図 System.Runtime参照プロパティ
上図でバージョンを見ると “4.1.2.0” だ。
たぶん、この辺りの数字の違いがエラーの原因なのだ。
newVersionの数字を増やしてみる
下図に示すように、念のためにオリジナルの<dependentAssembly>要素</dependentAssembly>の部分はコメント化して残しておいて、その下に新しい設定を追加した。
図 System.RuntimeのdependentAssembly設定でnewVersion属性を変更した
その部分を以下に引用する。
<!--dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1.1" /> </dependentAssembly--> <dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.3" /> </dependentAssembly>
つまりまあ、newVersionの数字を “4.3” にしてみた。
そして、再びVisual Studioでデバッグ無し実行ボタンをクリックしてこのASP.NET MVCサイトを表示してみたところ無事に表示出来た!
まあ解決してみるとあっけない。
ただし、なぜこれで解決したのか実はワテは良く分かっていない。
全然あかんがなw
どんなnewVersionの値なら成功するのか試す
なお、newVersionの値をいろいろ変えてみて実験した結果を以下に示す。
成功した例
newVersionが以下のような数字の場合にはASP.NET MVCのサイトは正常に表示出来た。
<bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.2" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.2" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.3" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="10.0" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="1234567890.0" />
と言う事は、兎に角デカイ数字を小数点の形式で指定しておけばいいのか?
逆に言うと、何故こんなnewVersionなんて言う属性が必要になるのか全く分からない。
失敗した例
一方、以下のようにnewVersionを設定すると新しいエラーが出た。
<bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="1234567890" />
そのエラーメッセージは以下の通り。
エラー アプリケーション構成ファイル “Web.config” は無効です。newVersion 属性の解析中に問題が発生しました。バージョン文字列の部分が短すぎるか、または長すぎます。
バージョンの数字には小数点が必要みたいだ。
と言う事で、冒頭で示したようなASP.NET MVC実行時のアプリケーションサーバーエラーが表示された場合には、該当のアセンブリ(今のケースではSystem.Runtime)のdependentAssemblyの定義行で、newVersion属性を修正すると治る場合が多い。
<dependentAssembly> 要素とは何か?
さて、何も分からずにdependentAssembly定義行を弄っていても進歩しないので、少しは調べておこう。
マイクロソフトのサイトから<dependentAssembly> 要素の説明を引用させて頂くと以下の通り。
各アセンブリのバインディング ポリシーとアセンブリの場所をカプセル化します。
dependentAssembly
アセンブリごとに1つの要素を使用します。<configuration>
<runtime>
<assemblyBinding>
<dependentAssembly>
まあ、こんなカタカナの専門用語が羅列された説明を読んでもワテには何のこっちゃ全く理解出来ない。
今回の例では、dependentAssemblyの中に指定出来る以下の属性のうち、bindingRedirectの値を変更した訳だ。
要素 | 説明 |
---|---|
assemblyIdentity |
アセンブリに関する識別情報を格納します。 この要素は、各要素に含める必要があり dependentAssembly ます。 |
codeBase |
ランタイムがコンピューターにインストールされていない場合に、共有アセンブリを見つけることができる場所を指定します。 |
bindingRedirect |
1 つのアセンブリ バージョンを別のバージョンにリダイレクトします。 |
publisherPolicy |
ランタイムがこのアセンブリの発行者ポリシーを適用するかどうかを指定します。 |
引用元 https://docs.microsoft.com/ja-jp/dotnet/framework/configure-apps/file-schema/runtime/dependentassembly-element
<runtime> の <assemblyIdentity> 要素とは何か?
dependentAssemblyの中に指定出来るもう一つの属性のassemblyIdentityでは、name属性 “System.Runtime” を指定している。
先ほど示した変更後のXML(newVersionの数字を “4.3” にしたやつ)を再び示す。
<!--dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.1.1.1" /> </dependentAssembly--> <dependentAssembly> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.1.1" newVersion="4.3" /> </dependentAssembly>
ここで、assemblyIdentity定義行に指定出来る属性は以下の通り。
属性 | 説明 |
---|---|
name |
必須の属性です。 アセンブリの名前 |
culture |
省略可能な属性です。 アセンブリの言語と国/地域を指定する文字列。 |
publicKeyToken |
省略可能な属性です。 アセンブリの厳密な名前を指定する16進値。 |
processorArchitecture |
省略可能な属性です。 プロセッサ固有のコードを含むアセンブリのプロセッサアーキテクチャを指定する、”x86″、”amd64″、”msil”、または “ia64” のいずれかの値。 値の大文字と小文字は区別されません。 属性に他の値が割り当てられている場合は、 <assemblyIdentity> 要素全体が無視されます。 以下を参照してください。ProcessorArchitecture |
引用元 https://docs.microsoft.com/ja-jp/dotnet/framework/configure-apps/file-schema/runtime/assemblyidentity-element-for-runtime
まあ、publicKeyToken=”b03f5f7f11d50a3a” なんてもう何のこっちゃ!?と言う感じだ。
上の説明を読むなら、”System.Runtime”と言うアセンブリ名に、それを一意に識別出来る数字 “b03f5f7f11d50a3a” が割り当てられていると言う事か。
何故そんな数字が必要なのだろう?
“System.Runtime”と言う名前があるなら、その名前だけで良いと思うのだが。
それにも係わらずこんなパスワードみたいな意味不明な数字を開発者が意識しなくてはならない理由が分からない。
兎に角、全く分からないw
<bindingRedirect> 要素
ついでに<bindingRedirect> 要素に指定出来る属性も引用しておこう。
属性 | 説明 |
---|---|
oldVersion |
必須の属性です。
初めに要求されていたアセンブリのバージョンを指定します。 アセンブリバージョン番号の形式は major. minor. build. revisionです。 このバージョン番号の各部分で有効値は、0 ~ 65535 です。 バージョン範囲は、次の形式でも指定できます。 n. n. n. n. n. n. n. n. n |
newVersion |
必須の属性です。
最初に要求されたバージョンの代わりに 、次の形式で使用するアセンブリのバージョンを指定します。 n. n. n. n この値では |
引用元 https://docs.microsoft.com/ja-jp/dotnet/framework/configure-apps/file-schema/runtime/bindingredirect-element
まとめ
当記事では、ASP.NET MVCでウェブサイトを開発していて時々遭遇する「実行時のアプリケーションでサーバー エラー」の解決方法を紹介した。
あくまでASP.NET MVC初心者のワテの経験に基づくやり方なので、正統派の手法かどうかは不明だ。
ワテの経験では、この手のエラーが出た場合には、まずはWeb.configファイルを修正すれば多くの場合解決出来る。
あるいはNuget Package Managerでパッケージを更新するなども必要になる場合もあると思うが、全部の対策を一度にやると訳分からなくなるので、闇雲にやるのではなくて、Web.configファイルの修正から始めると良いと思う。
しかしながら、根本的な疑問として、今まで問題無く開けて実行出来ていたプロジェクトが、Visual Studioのバージョンが上がると何故こんなエラーが出る事があるのだろうか?
分からない。
正しいエラーメッセージとは何か?
それにしても、冒頭で示した数十行に渡るエラーメッセージは、初めて見る人にはさっぱり分からないと思う。以下に、その冒頭部分を再び引用する。
ファイルまたはアセンブリ ‘System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
このエラーメッセージに示されたキーワードを元に、必死でネット検索すれば解決の糸口は見付かるとは思うのだが、あまり親切なエラーメッセージには思えない。
- なぜこのエラーが出たのか?
- System.Runtimeアセンブリまたはその依存関係を読み込もうとしたのはどの設定に基づくのか?
- 指定したファイルとはどのファイルなのか?
- で、結局どうしたら解決する可能性があるのか?
など、何ら記載されていないのだ。
ワテが考える正しいエラーメッセージとは、エラーの内容を説明するだけでなく、その理由や考えられる解決策まで表示すべきである。
今の場合なら、
と言った説明でも有れば、大きなヒントになるのだが。
まあ、マイクロソフトさんがこのブログ記事を見て、そんな対策をして頂けると嬉しいのだが。
ワテが使っているWindows VPSはこちら↴
ワテが読んでいない教科書はこちら↴
いつか読みたい。
コメント