ASP.NET Core MVC の .NET Core アセンブリは何処からロードされるのか?

結論から言うと、

  • C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0 配下
  • C:\Users\<ユーザー名>\.nuget\packages

からロードされています。ASP.NET Core MVC アプリを動作させると、以下の分だけロードされている。

File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\Microsoft.CodeAnalysis.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\Microsoft.Win32.Registry.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\mscorlib.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.AppContext.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Buffers.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Collections.Concurrent.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Collections.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.ComponentModel.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Console.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Diagnostics.Debug.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Diagnostics.DiagnosticSource.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Diagnostics.Tracing.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Dynamic.Runtime.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Globalization.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.IO.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.IO.FileSystem.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.IO.FileSystem.Primitives.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.IO.FileSystem.Watcher.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Linq.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Linq.Expressions.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Net.Primitives.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Numerics.Vectors.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.ObjectModel.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Private.CoreLib.ni.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Private.Uri.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.Emit.ILGeneration.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.Emit.Lightweight.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.Extensions.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.Primitives.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Reflection.TypeExtensions.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Runtime.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Runtime.Extensions.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Runtime.Handles.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Runtime.InteropServices.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Runtime.InteropServices.RuntimeInformation.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Text.Encoding.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Text.Encoding.Extensions.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.Overlapped.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.Tasks.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.Thread.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.ThreadPool.dll
File	C:\Program Files\dotnet\shared\Microsoft.NETCore.App\1.0.0\System.Threading.Timer.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Antiforgery\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Antiforgery.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Authorization\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Authorization.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Cors\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Cors.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Cryptography.Internal\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Cryptography.Internal.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.DataProtection.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.DataProtection.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.DataProtection\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.DataProtection.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Diagnostics\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Diagnostics.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Hosting.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Hosting.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Hosting.Server.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Hosting.Server.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Hosting\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Hosting.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Http.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Http.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Http.Features\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Http.Features.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Http\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Http.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Mvc.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.ApiExplorer\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.ApiExplorer.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Core\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.Core.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Cors\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.Cors.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.DataAnnotations\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.DataAnnotations.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Formatters.Json\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.Formatters.Json.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Razor.Host\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.Razor.Host.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.Razor\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.Razor.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.TagHelpers\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.TagHelpers.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc.ViewFeatures\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.ViewFeatures.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Mvc\1.0.0\lib\netstandard1.6\Microsoft.AspNetCore.Mvc.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Razor.Runtime\1.0.0\lib\netstandard1.5\Microsoft.AspNetCore.Razor.Runtime.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Razor\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Razor.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Routing.Abstractions\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Routing.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Routing\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Routing.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Server.IISIntegration\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Server.IISIntegration.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.Server.Kestrel\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.Server.Kestrel.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.AspNetCore.StaticFiles\1.0.0\lib\netstandard1.3\Microsoft.AspNetCore.StaticFiles.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.DotNet.InternalAbstractions\1.0.0\lib\netstandard1.3\Microsoft.DotNet.InternalAbstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Caching.Abstractions\1.0.0\lib\netstandard1.0\Microsoft.Extensions.Caching.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Caching.Memory\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Caching.Memory.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Configuration.Abstractions\1.0.0\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Configuration.EnvironmentVariables\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Configuration.EnvironmentVariables.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Configuration.FileExtensions\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Configuration.FileExtensions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Configuration.Json\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Configuration.Json.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Configuration\1.0.0\lib\netstandard1.1\Microsoft.Extensions.Configuration.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.DependencyInjection.Abstractions\1.0.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.DependencyInjection\1.0.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.DependencyModel\1.0.0\lib\netstandard1.6\Microsoft.Extensions.DependencyModel.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.FileProviders.Abstractions\1.0.0\lib\netstandard1.0\Microsoft.Extensions.FileProviders.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.FileProviders.Physical\1.0.0\lib\netstandard1.3\Microsoft.Extensions.FileProviders.Physical.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Localization.Abstractions\1.0.0\lib\netstandard1.0\Microsoft.Extensions.Localization.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Logging.Abstractions\1.0.0\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Logging.Console\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Logging.Console.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Logging.Debug\1.0.0\lib\netstandard1.3\Microsoft.Extensions.Logging.Debug.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Logging\1.0.0\lib\netstandard1.1\Microsoft.Extensions.Logging.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.ObjectPool\1.0.0\lib\netstandard1.3\Microsoft.Extensions.ObjectPool.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Options\1.0.0\lib\netstandard1.0\Microsoft.Extensions.Options.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.PlatformAbstractions\1.0.0\lib\netstandard1.3\Microsoft.Extensions.PlatformAbstractions.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.Primitives\1.0.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Extensions.WebEncoders\1.0.0\lib\netstandard1.0\Microsoft.Extensions.WebEncoders.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.Net.Http.Headers\1.0.0\lib\netstandard1.1\Microsoft.Net.Http.Headers.dll
File	C:\Users\masuda\.nuget\packages\Microsoft.VisualStudio.Web.BrowserLink.Loader\14.0.0\lib\netstandard1.5\Microsoft.VisualStudio.Web.BrowserLink.Loader.dll
File	C:\Users\masuda\.nuget\packages\Newtonsoft.Json\9.0.1\lib\netstandard1.0\Newtonsoft.Json.dll
File	C:\Users\masuda\.nuget\packages\System.Collections.NonGeneric\4.0.1\lib\netstandard1.3\System.Collections.NonGeneric.dll
File	C:\Users\masuda\.nuget\packages\System.Collections.Specialized\4.0.1\lib\netstandard1.3\System.Collections.Specialized.dll
File	C:\Users\masuda\.nuget\packages\System.ComponentModel.Primitives\4.1.0\lib\netstandard1.0\System.ComponentModel.Primitives.dll
File	C:\Users\masuda\.nuget\packages\System.ComponentModel.TypeConverter\4.1.0\lib\netstandard1.5\System.ComponentModel.TypeConverter.dll
File	C:\Users\masuda\.nuget\packages\System.Runtime.Serialization.Primitives\4.1.1\lib\netstandard1.3\System.Runtime.Serialization.Primitives.dll
File	C:\Users\masuda\.nuget\packages\System.Text.Encodings.Web\4.0.0\lib\netstandard1.0\System.Text.Encodings.Web.dll

ASP.NET 廻りと MVC 廻りがごっそり NuGet からダウンロードされていることが分かります。たぶん、これプラス、自前の NuGet パッケージが入るのだと思う。

ロードしているアセンブリのチェックは、Process Explorer で。

image

ちなみに、F# 版の ASP.NET MVC アプリを作ると、

File	C:\Users\masuda\.nuget\packages\Microsoft.FSharp.Core.netcore\1.0.0-alpha-160629\lib\netstandard1.6\FSharp.Core.dll

だけが増えます。F# のアセンブリが NuGet で取れるようになっているので、以前のような PCL の混在であれこれと悩むことはなくなりそうな感じです。このあたりは、ぼちぼち調べていきます。

カテゴリー: ASP.NET | ASP.NET Core MVC の .NET Core アセンブリは何処からロードされるのか? はコメントを受け付けていません

HDD性能の再チェック

Hyper-V 上の Visual Studio 2015 の起動が、むちゃくちゃ遅いので、ちょっと SSD/HDD の性能を再チェックしておく。

HDDのアクセススピードは VMWare に影響するのか? | Moonmile Solutions Blog にある3年前の構成をまだ使っている(途中で SSD が逝ったので、交換しているが)ので、HDD が劣化していなか調べるのも兼ねている。SSD のほうは、2年半ぐらいで逝ってしまったので、なんとも言えないのだけど、極端に劣化している感じはしない。途中で、OS も変わっているので、10% 程度は誤差だろう。

image

Cドライブ (TS512)

image

Dドライブ (WDC)

image

Fドライブ (WDC)

image

Hドライブ (PLEXTOR)

image

D:iso (Hitachi)

image

F:dvd (Hitachi)

image

 

Fドライブ内のHyper-Vで

image

SSD(PLEXTOR)のHyper-Vで

– Windows 7

image

– Windows 10

image

Hyper-V 上で Visual Studio を動かす

SSD(PLEXTOR)とHDD(WDC)上に、Hyper-V で Windows 10 + Visual Studio 2015 の環境を作って起動速度を比較してみる。メモリは4GB を割り当てている。

imageimage

左が SSD で、右が HDD。基本性能が違うので、動作にも差がでるであろうというのは予想できるのだが、結論から言えば、

  • Visual Studio の初回起動時は SSD のほうが圧倒的に早い
  • 2回目以降の起動では、4GB のキャッシュに乗っているためか、速度は変わらない

初回起動の比較

2回目の起動比較

どうやら Windows の起動と同じで、初回のみ我慢すれば HDD もそこそこ SSD と変わらない程度で起動してくれるのが分かる。おそらく 4GB のメモリのキャッシュが効いているのだろう。

となると、メモリを 2GB のぎりぎりにした場合、2回目の起動にも差が出るのではないか、という仮定ができる…が、実際やってみると 2GB の場合でも、2回目の起動は対して違いはない。

とはいえ、別件で C++ のコンパイルをしていると、SSD のほうは若干早かったりするので、Hyper-V アクセスであってもストレージへのアクセススピードは早いほうがいい。いくつか ASP.NET MVC 絡みで Hyper-V の仮想環境を複数動かしているので、SSD 480 GB を追加購入。PLEXTOR のほうは、家庭用 PC に移そうと思うので、移行したらベンチマークを取るつもり。家庭用 PC は Core 2 に Windows 10 を入れているというとんでもなく遅い PC なのだが、ブラウザの閲覧ぐらいだったらこれで十分だったりする。それでも、かなりもたつくので、足回りを早くしたときの確認をしてみたい。

カテゴリー: パフォーマンス | HDD性能の再チェック はコメントを受け付けていません

ASP.NET Core MVC のデバッグ実行がとてつもなく遅くなった時

なんだか、分からないが、Visual Studio から IIS Express でデバッグ実行したときに、とてつもなく遅くなって 500 が頻発した。仮想環境のものや、白 vaio の別の環境では大丈夫なので、どうやら業務 PC の Visual Studio 2015 だけ妙な具合になったらしい。

↓なエラーがでる。このエラーが出るまでとてつもなく長い。

image

どうやら、Microsoft シンボルサーバーに問合せをしているところが遅いらしく、このチェックを外すと普通に早くなる。

image

再確認すると、他の PC では入っていなかったので、どっかのタイミングで自動的に入った模様。あるいは、昨日あたりからシンボルサーバーが落ちているとか?

もうひとつ、ASP.NET MVC のプロジェクト作って、別のフォルダーに移すと、IIS Express につながらなくてやっぱり 500 のエラーになる。エラーを見ると、構成ファイルが読み込めないとのことなのだが、プロジェクト内にある web.config は正常のような気がしないでもない。

image

実は、Visual Studio が作る .vs/config/applicationhost.config に IIS Express のための virtualDirectory 書き込まれてあって、フォルダーを移動したときにこの値が以前の場所を指しているので読み込めなくなる。なんだかなー。

というわけで、配布をしたりフォルダーを移動したときは、マメに .vs フォルダーを消す必要がある。このフォルダーは Visual Studio が IIS Express を起動時に再び作るので消し去っても大丈夫。

参考先

asp.net mvc – Visual Studio Debugging/Loading Very Slow – Stack Overflow
http://stackoverflow.com/questions/12567984/visual-studio-debugging-loading-very-slow

c# – ASP.NET 5 MVC: unable to connect to web server ‘IIS Express’ – Stack Overflow
http://stackoverflow.com/questions/35675747/asp-net-5-mvc-unable-to-connect-to-web-server-iis-express

カテゴリー: ASP.NET | 1件のコメント

CheckBox のチェックの□の色を変更する

いわゆる、↓の□の色を変更します。

image

普通にやると、黒で表示されて、どうにも変更できません。が、XAML のスタイルを使って変更します。これ、何をやっても黒にしかならなくて、このような白い四角を表示したいときには、別途自作するとかしていたんですが、原因がわかりました。

XAML ファイルを Blend で開く。

image

「テンプレートの編集」→「コピーして編集」を選択すると、<Page.Resources> にスタイルが追加される。

アニメーションしている storyboard の中で、

<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=&quot;Stroke&quot; Storyboard.TargetName=&quot;NormalRectangle&quot;>
   <DiscreteObjectKeyFrame KeyTime=&quot;0&quot; Value=&quot;{ThemeResource CheckBoxCheckBackgroundStrokeUncheckedPointerOver}&quot;/>
</ObjectAnimationUsingKeyFrames>

の3行をごっそり消します。
どうやら、ここで設定している CheckBoxCheckBackgroundStrokeUncheckedPointerOver などのリソースが「Black」固定になっているんですよね。テーマ色なので仕方がないんですが、ここの Storoke の色は、Setter Property を使って欲しかったところですね。

<Grid Height=&quot;32&quot; VerticalAlignment=&quot;Top&quot;>
    <Rectangle 
        x:Name=&quot;NormalRectangle&quot; 
        Fill=&quot;{ThemeResource CheckBoxCheckBackgroundFillUnchecked}&quot; 
        Height=&quot;20&quot; 
        Stroke=&quot;White&quot; 
        StrokeThickness=&quot;{ThemeResource CheckBoxBorderThemeThickness}&quot; UseLayoutRounding=&quot;False&quot; Width=&quot;20&quot;/>
    <FontIcon x:Name=&quot;CheckGlyph&quot; Foreground=&quot;{ThemeResource CheckBoxCheckGlyphForegroundUnchecked}&quot; FontSize=&quot;20&quot; FontFamily=&quot;{ThemeResource SymbolThemeFontFamily}&quot; Glyph=&quot;&#xE001;&quot; Opacity=&quot;0&quot;/>
</Grid>

でもって、最後のほうにある、NormalRectangle を見つけて、Stroke=”White” で色を設定しておきます。ここだけ設定しても、先の storyboard がそのままだと色が上書きされているのが、色が変わらない原因です。

要は、こんな感じなのを作りたかっただけです。

image

ちなみ、デザイン時のデータバインドを、

d:DataContext="{d:DesignData Source=/data/sample.json, Type=vm:MyViewModel}"

な感じで設定しているのですが、色のテーマを Dark にしようとして、Page タグに RequestedTheme=”Dark” を設定すると、↓な感じにバインドエラーがでます。

image

RequestedTheme を消すと、↓な感じでデザイン時のバインドが効くので、バグっぽいです。

image

なんだかなー。あとで調査してバグ報告しますか。

カテゴリー: C#, XAML | CheckBox のチェックの□の色を変更する はコメントを受け付けていません

俺のラズパイ3で.NET CoreなF#が動かないわけがない

と、思っていましたが、動きません orz.

dotnet/coreclr:
https://github.com/dotnet/coreclr/
dotnet/cli:
https://github.com/dotnet/cli

.NET Coreをビルドしようと思うと、

な感じで拒否られるし、じゃあ、dotnet コマンドだけでもビルドできないかと思うと、

20160831_06

な感じで、前回ビルド済みの dotnet コマンドを要求されるし、どうにもなりません。

Problem install .Net Core on Raspberry Pi Model B ・ Issue #140 ・ dotnet/core
https://github.com/dotnet/core/issues/140
How to use dotnet cli in debian linux on arm. ・ Issue #3197 ・ dotnet/cli
https://github.com/dotnet/cli/issues/3197

を見る限り、Raspberry Pi で使われてる ARMv7 ではビルドできませんという具合です。.NET CoreのターゲットとしてARMは入っているので、いずれ対応するのかという雰囲気だけはあるんですが、進展はしているのかは不明。

でも、元 dnvm は動いていたよね?

しかし、Raspberry Pi2 に Windows IoT Core がインストールできるわけだし、最初から ARMv7 が開発ターゲットから外されているというのも変名は話なので、いつくか遡ってみると、

Raspberry Pi 2 にインストールした Windows 10 IoT Core 上で DNX と ASP.NET 5 アプリケーションを動かしてみた – しばやん雑記
http://blog.shibayan.jp/entry/20150629/1435546231
How to run .NET Core Application on Pi2 – Win10 IoT Core? – Damir Dobric Posts – developers.de
http://developers.de/blogs/damir_dobric/archive/2015/09/22/how-to-run-net-core-application-on-pi2-win10-iot-core.aspx

を読むと、dotnet コマンドの前身の dnvm コマンドでは Raspberry Pi で(Windows IoT Core限定カモ)動いていた模様です。
「模様」ですというのは、現在、ARMのランタイムを取ろうとすると、エラーが発生します。

dnvm install latest -r coreclr -arch x64 は通るけど、
dnvm install latest -r coreclr -arch ARM は通らなくなっているので、

サーバー自体から消えてしまった感じ。うーん、古いバイナリでよいので残してほしかったのですが、ここが OSS の辛いところ。

ややこしい、3つの Core な関係

でもって、やりたいことは、F# な Web APIアプリをラズパイ上で動かしたいだけなので、別に mono でもいいんだよな、と思い直して .NET Core なところをあれこれと弄っていこうとすると、

– .NET Core なランタイム
– ASP.NET Core
– ASP.NET Core MVC

な、ややこしい関係にぶち当たります。

素直に、.NET Core 環境だけで作るとか、素直に .NET Framework 4.6 な環境で作るとかすればよいのですが、じゃあ、dotnet コマンドは何の環境で動いているのかとか、ASP.NET Core MVC は内部で何がうごいているのかとかとか考えるとややこしいのです。

msbuild に代わるビルド環境としての dotnet コマンドは、dotnet restore してアセンブリを NuGet で取ってきて dotnet run でプロジェクトを実行できます。プロジェクトがある場所に、project.json があって、その中に設定が書いてあるわけですが、この dotnet コマンドが、.NET Core 上なのか .NET Framework 上なのか mono 上なのかが謎です。ただ、dotnet/cli を取って来て Raspi 上でビルドができない(Ubuntu上ではできた)ので、動作はともかくとして、ビルドするのに「動作する dotnet」が必要です。ブートストラップ的になっているの、卵と鶏の関係になっています。

mono 上で動く dotnet コマンドがあればいいのに、と思うんだけど、それがないところみると、たぶん .NET Core 上で動いているんですよね、おそらく。

ASP.NET Core はオープンソースな https://github.com/aspnet/ で開発が進められていますが、内部的に .NET Core 必須なのか?というとそうではなくて、

を見ると、

– ASP.NET Web Application(.NET Frameowork)
– ASP.NET Core Web Application(.NET Core)
– ASP.NET Core Web Application(.NET Framework)

の3種類のASP.NET MVCプロジェクトが作れます。

最初のASP.NETアプリは、従来のASP.NET MVC5とかを作るプロジェクトで .NET Frameworkを使うやつです。
次の、(.NET Core)のほうは、.NET Coreを使っているほうで、dotnet runとかで実行ができるやつです。IISがいらないのでLinuxに移すことができますよね。
じゃあ、最後の「ASP.NET Core Web Application(.NET Framework)」ってのは何でしょう?
.NET Framework上で動く、ASP.NET Core って何?ASP.NET Coreの「Core」って、.NET Coreの「Core」じゃなかったの?と思う訳ですが、どうやら、この2つの Core は違う意味とだということが分かります。どうやら、.NET Framworkを使って、IISのいらない exe ファイルを作って実行することができるですよね。

bin/Dbug/net452/win7-x64 フォルダーにある WebApplication1.exe を実行すると、普通に ASP.NET Core MVC アプリケーションが動きます。

このバイナリをそのまま Raspberry pi に持って行って、mono で動かすと libuv のアセンブリがなくて動きません。Raspberry pi 上に libuv はインストールしたのですが、Linux の動的ライブラリ *.so を探しているかどうかは不明です。

どうやら、ASP.NET MVC のほうは、IIS 上で動いて、
ASP.NET Core MVC のほうは

– Kestrel
– libuv

が肝だということが薄々わかってきました。Kestrel は Webサーバーで、https://github.com/aspnet/KestrelHttpServer からダウンロードが可能です。ただし、これをビルドするときに dotnet コマンドが必要なので、ラズパイ上では頓挫しています。「ASP.NET Core Web Application(.NET Framework)」のプロジェクトが、Kestrel を使っているので、.NET Core 自体は必要ないはずなんですよね(ASP.NET Coreしか必要ない)。
libuv のほうは、Node.js でも使われているクロスプラットフォームな TCP/IP ライブラリです。こっちのほうは、https://github.com/libuv/libuv からダウンロードしてラズパイ上でもビルドができます。

ということは、「dotnet コマンドを使わないでビルドができる Kestrel を .NET Framework 版でビルドすれば、mono 上で動く」ので、ラズパイ上で動くようになるのでは?と思いついたのですがどうでせふ?

オレオレASP.NET Core MVCを作成する

ひとまず、HttpListenerを使ったら mono でも動くので、Raspberry Pi 上に F# な Web APIサーバーが立てられるのではと思って、ちまちまと考えていたり。

type WebHostBuilder() as self =
    let mutable _sv = new System.Net.HttpListener()

    member x.UseStartup() = 
        self
    member x.UseUrls(url) = 
        _sv.Prefixes.Add( url )
        self
    member x.Build() = 
        ()
    member x.Run() =
        if _sv.Prefixes.Count = 0 then
            _sv.Prefixes.Add(&quot;http://localhost:5000/&quot;)
        _sv.Start()
        while true do
            let cont = _sv.GetContext()
            Console.WriteLine(&quot;url: {0}&quot;, cont.Request.Url)
            let sw = new System.IO.StreamWriter(cont.Response.OutputStream)
            sw.WriteLine(&quot;OK&quot;)
            sw.Close()
            cont.Response.Close()
        _sv.Stop()

[<EntryPoint>]
let main argv = 
    printfn &quot;start oreoreHttpServer&quot;

    let host = new WebHostBuilder()
    host.UseStartup()
        .UseUrls(&quot;http://+:5000/&quot;)
        .Build()
    host.Run()
    0

なんとなく、インターフェースだけ Kestrel に似せています。

カテゴリー: ASP.NET, F#, RaspberryPi | 俺のラズパイ3で.NET CoreなF#が動かないわけがない はコメントを受け付けていません

F#でASP.NET CoreのWeb APIを作ろう(SQLite編)

F#でASP.NET CoreのWeb APIを作ろう | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7996

の続きで、データベースにSQLiteを使ってWeb APIを作成します。
.NET Core用のSQLite は NuGet で「”Microsoft.EntityFrameworkCore.Sqlite”」があるので、これを使えばokです。
C#の.NET CoreのWeb APIプロジェクトからコピーしたあとから、再スタート。

project.jsonの修正

– “includeFiles” にF#のファイルをひとつずつ追加(面倒だけど)

– “dependencies” に4つの参照をを追加
– “Microsoft.EntityFrameworkCore”
– “Microsoft.EntityFrameworkCore.Sqlite”
– “Microsoft.EntityFrameworkCore.Sqlite.Design”
– “Microsoft.EntityFrameworkCore.Tools”

C#のときは “Microsoft.EntityFrameworkCore” が無くても動くのだけど、F#のプロジェクトには入れます。でも、入れるのが普通な気がする。

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true,
    "debugType": "portable",
    "compilerName": "fsc",
    "compile": {
      "includeFiles": [
        "Models/Person.fs",
        "Data/ApplicationDbContext.fs",
        "Controllers/ValuesController.fs",
        "Controllers/PeopleController.fs",
        "Startup.fs",
        "Program.fs"
      ]
    }
  },
  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
    "dotnet-compile-fsc":"1.0.0-preview2-*"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ],
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        },
        "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-160629"
      }
    }
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.EntityFrameworkCore": "1.0.0",
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0",
    "Microsoft.EntityFrameworkCore.Sqlite.Design": "1.0.0",
    "Microsoft.EntityFrameworkCore.Tools":  "1.0.0-preview2-final"
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "Areas/**/Views",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

データベース接続の設定

appsettings.json に “DefaultConnection” を追加。ローカルファイルにSQLiteのデータベースを置きます。

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Filename=./sample.db"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

StartupクラスのConfigureServicesメソッドでSQLiteに接続できるようにする。

type Startup(env:IHostingEnvironment) as this =
    do 
        let builder = new ConfigurationBuilder()
        builder.SetBasePath(env.ContentRootPath)
                .AddJsonFile(&quot;appsettings.json&quot;, true, true)
                .AddJsonFile(&quot;appsettings.&quot; + env.EnvironmentName + &quot;.json&quot;, true)
                .AddEnvironmentVariables() |> ignore
        this.Configuration <- builder.Build()

    [<DefaultValue>]
    val mutable _Configuration:IConfigurationRoot 
    member val Configuration:IConfigurationRoot = null with get, set
    member x.ConfigureServices( services:IServiceCollection ) =
        services.AddDbContext<ApplicationDbContext>( 
            fun options ->
                options.UseSqlite(this._Configuration.GetConnectionString(&quot;DefaultConnection&quot;)) |> ignore
                ) |> ignore
        services.AddMvc() |> ignore
        ()

    member x.Configure( app:IApplicationBuilder, env:IHostingEnvironment, loggerFactory:ILoggerFactory ) =
        loggerFactory.AddConsole(this._Configuration.GetSection(&quot;Logging&quot;)) |> ignore
        loggerFactory.AddDebug() |> ignore
        app.UseMvc() |> ignore
        ()

Modelの作成

シンプルに自動生成のプロパティを使えます。

Person.cs

type Person() =
    member val Id = 0 with get, set
    member val Name = "" with get, set
    member val Age = 0 with get, set

ApplicationDbContextの作成

namespace ApiFSharp.Data
open Microsoft.EntityFrameworkCore
open ApiFSharp.Models

type ApplicationDbContext( options:DbContextOptions<ApplicationDbContext> ) =
    inherit DbContext( options )
    [<DefaultValue>] 
    val mutable _Person:DbSet<Person> 
    member x.Person with get() = x._Person and set value = x._Person <- value
    override x.OnModelCreating( builder:ModelBuilder ) =
        base.OnModelCreating(builder)
        ()

実は、「member x.Person with …」のところで随分悩みました。
自己参照のつもりで、this を追加して this._Person で参照させると実行時に、循環参照のエラーがでます。

type ApplicationDbContext( options:DbContextOptions<ApplicationDbContext> ) as this=
    inherit DbContext( options )
    [<DefaultValue>] 
    val mutable _Person:DbSet<Person> 
    member x.Person with get() = this._Person and set value = this._Person <- value

http://localhost:5000/api/people で、Web APIにアクセスしようとすると、System.InvalidOperationException の例外が発生しています。

Hosting environment: Production
Content root path: D:workblogsrcSQLiteWebApiSQLiteWebApiSQLiteFSharp
Now listening on: http://*:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/api/people
fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HKUCHBOFLSDB": An unhandled exception was thrown by the application.
System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed
 recursively before it was fully initialized.
   at Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicFunctions.FailInit()
   at ApiFSharp.Data.ApplicationDbContext.set_Person(DbSet`1 value)
   at Microsoft.EntityFrameworkCore.Internal.DbSetInitializer.InitializeSets(DbContext context)
   at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options)
   at ApiFSharp.Data.ApplicationDbContext..ctor(DbContextOptions`1 options)
...

これを this 使わずに

type ApplicationDbContext( options:DbContextOptions<ApplicationDbContext> ) =
    inherit DbContext( options )
    [<DefaultValue>] 
    val mutable _Person:DbSet<Person> 
    member x.Person with get() = x._Person and set value = x._Person <- value

とすると循環しないので、なんか理由があるんですかね?謎です。

Controllerの作成

まめに ignore を追加します。

[<Route(&quot;api/[controller]&quot;)>]
type PeopleController(context) =
    inherit Controller() 
    let _context:ApplicationDbContext = context

    [<HttpGet>]
    member x.Get () = 
        if isNull _context.Person then  
            new List<Person>()
        else
             _context.Person.ToList()
    [<HttpGet(&quot;{id}&quot;)>]
    member x.Get (id:int) = 
        _context.Person.SingleOrDefault( fun m -> m.Id = id )
    [<HttpPost>]
    member x.Post([<FromBody>] person:Person) =
        _context.Add( person ) |> ignore
        _context.SaveChanges() |> ignore
        person.Id
    [<HttpPut(&quot;{id}&quot;)>]
    member x.Put( id:int, [<FromBody>] person:Person) =
        if id <> person.Id then
            -1
        else 
            _context.Update( person ) |> ignore
            _context.SaveChanges() |> ignore
            person.Id
    [<HttpDelete(&quot;{id}&quot;)>]
    member x.Delete( id:int ) =
        let person = _context.Person.SingleOrDefault( fun m -> m.Id = id )
        _context.Person.Remove( person ) |> ignore
        _context.SaveChanges() |> ignore
        person.Id

ビルドして実行

実行するとこんな感じです。データベースのファイル「sample.db」は自動では作ってくれないので、何らかの形であらかじめファイルを作っておきます。

サンプルコード

サンプルコードはこちら。C#版も含めてあります。
https://1drv.ms/u/s!AmXmBbuizQkXgfwM4A7bz3cWyThD_Q

次は…

あれこれ追加が必要ですが、ASP.NET Coreな環境で、F#でWeb APIが作れることがわかりました。本当はASP.NET MVCにしたいけど、ViewページのRazorが対応していないので無理かな。このあたりは後で調べます。
開発環境は Visual Studio Codeを使う(というか、むしろ Visual Studio 2015上では開発できない)ので、LinuxはMac上でも開発&動作ができます。IISとかApacheとかに関係なく、マイクロサーバー的にWeb APIを提供できる環境ができるのはよいかもしれません。Azureに乗せたりすると、自前のなんかのプロキシとか作れるんじゃないですかね。いや、C#で書いたほうが楽かもしれませんが、もうちょっとなんか整理して、Controllerだけさくさくと実験&拡張できるようにすると、手軽なWeb APIが作れるかも。

カテゴリー: ASP.NET, F#, SQLite | F#でASP.NET CoreのWeb APIを作ろう(SQLite編) はコメントを受け付けていません

ASP.NET Core MVC で SQLite を使う

.NET Core上でF#とSQLiteを結び付ける前哨戦として、ASP.NET Core MVC で SQLite を使ってみます。
方法としては、Visual Studio で「ASP.NET Core Web Applicaiton(.NET Core)」は、SQL Serverを使う設定になっているので、これをSQLiteに切り替えるだけです。

NuGetでSQLiteをインストール

– Microsoft.EntityFrameworkCore.SQLite
– Microsoft.EntityFrameworkCore.SQLite.Desgin

の2つをインストールします。Desginのほうは、データベースファーストのほうで使うけど、一応インストール。

SQLiteの設定に切り替え

StartupクラスのConfigureServicesメソッドで、UseSqlServerの箇所をUseSqliteに切り替え。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
    // Add framework services.
    services.AddMvc();
}

appsettings.jsonを開いて、DefaultConnectionの値をSQLiteが使うファイル名にする。

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Filename=./sample.db"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Modelクラスを作って、スキャフォールディング

あとは、SQL Serverのときと同じで、ModelクラスになるPersonクラスを使って、スキャフォールディングを実行。

namespace SampleSQLiteMvc.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

マイグレーションとデータベースのアップデートをすれば完了です。

dotnet ef migrations add init
dotnet ef database update

実行

実行するとこんな感じに、SQL Serverのときと同じように動きます。

肝は、NuGet で Microsoft.EntityFrameworkCore.SQLite を入れて、Startupクラスで接続先を変えるだけなので、それほど難しくありません。

サンプルコード

サンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfwL7jS1NrKxv8KbQg

カテゴリー: ASP.NET, SQLite | ASP.NET Core MVC で SQLite を使う はコメントを受け付けていません

F#でASP.NET CoreのWeb APIを作ろう

なぜ、F#で作るのかはさておき、C#のWeb APIアプリをF#にコンバートしてみます。本当のところは、ASP.NET Core MVCのほうをF#に対応させてみたかったのですが、Razor構文がF#に(たぶん)対応していないので無理ってことで、ViewページのないWeb APIで試しています。まあ、F#の関数型なところがステートレスなHTTPプロトコルに合っているんじゃないか、常々思ってはいるのですが。

いわゆる、ASP.NET Core Web Application(.NET Core) をF#に直します。

単純なWebアプリのほうは、いくつかサンプルが見つかるのですが、Web APIのコンバートは見つからなかったので参考になると思います。先行きは、SQLiteを使って、データベースのアクセスまで作りたいところ。

Visual Studio Codeの準備

実は、F#の.NET Coreは、Visual Studio 2015は対応していません。コードのコンバートなのでちまちまとテキストエディタを使ってもよいのですが、さすがにインテリセンスがないとF#のコードは組みづらい。

Visual Studio Code
https://www.visualstudio.com/ja-jp/products/code-vs.aspx

を使うと、.NET CoreのF#でもインテリセンスが使えるようになります。ちなみに、LinuxとMacでもF#の.NET Core環境が使えます。

Visual Studio Codeをダウンロード&インストールして「表示」→「Extentions(拡張)」から、「Ionide-fsharp」をインストールします。
Windows版の場合は、Ionide-fsharpだけでよいのですが、LinuxとMacの場合は別途 mono もインストールします。拡張機能が mono を使っているからだと思うのだけど、そのうち .NET Coreに統一されるかもしれません。

インストール後に「有効」にして、VSCode を再起動すると、F#のコードでインテリセンスが使えるようになります。

C#の.NET CoreのWeb APIプロジェクトからコピー

Visual Studio 2015で.NET CoreのWeb APIプロジェクトを作って、C#のコード(Startup.cs、Program.cs、ValuesController.cs)以外のファイルをごっそりコピーします。

project.jsonの修正

コンソール版のF#コードを「dotnet new –lang F#」で作成して、2つのproject.jsonをうまくマージします。
F#の場合は、ビルドするときに前方参照が必要なので、”buildOptions” – “compile” – “includeFiles” にビルドする順番ファイル名を書いておきます。これが非常に面倒なんですが…先行きはどうなんでしょう?

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true,
    "debugType": "portable",
    "compilerName": "fsc",
    "compile": {
      "includeFiles": [
        "Controllers/ValuesController.fs",
        "Startup.fs",
        "Program.fs"
      ]
    }
  },
  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
    "dotnet-compile-fsc":"1.0.0-preview2-*"
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ],
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        },
        "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-160629"
      }
    }
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0"
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "Areas/**/Views",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

コードをF#に作り変える

3つのC#のファイルをF#にコンバートします。
大体、1対1でコンバートができるのですが、F#のほうが型チェックが厳しいので、戻り値が void になる関数はまめに ignore していきます。戻り値が推論されて、実行時にメソッドが見つからなくてエラーになるのがハマりどころです。

Startup.cs

namespace ApiFSharp

open System
open System.Collections.Generic
open System.Linq
open System.Threading.Tasks
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.Configuration
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Logging

type Startup(env:IHostingEnvironment) =
    let mutable _Configuration:IConfigurationRoot = null
    do 
        let builder = new ConfigurationBuilder()
        builder.SetBasePath(env.ContentRootPath)
                .AddJsonFile(&quot;appsettings.json&quot;, true, true)
                .AddJsonFile(&quot;appsettings.&quot; + env.EnvironmentName + &quot;.json&quot;, true)
                .AddEnvironmentVariables() |> ignore
        _Configuration <- builder.Build()
    member x.Cnfiguration 
        with get() =  _Configuration 
        and set( value ) = _Configuration <- value
    member x.ConfigureServices( services:IServiceCollection ) =
        services.AddMvc() |> ignore
        ()
    member x.Configure( app:IApplicationBuilder, env:IHostingEnvironment, loggerFactory:ILoggerFactory ) =
        loggerFactory.AddConsole( _Configuration.GetSection(&quot;Logging&quot;)) |> ignore
        loggerFactory.AddDebug() |> ignore
        app.UseMvc() |> ignore
        ()

Web APIをどのPCからも受け付けられるように UseUrlsメソッドで「”http://*:5000″」のように追加しておきます。こうすると、Linux仮想マシンでWeb APIを動かして、PCのブラウザでチェックする、ということができます。

Program.fs

open System
open System.Collections.Generic
open System.IO
open System.Linq
open System.Threading.Tasks
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Builder
open ApiFSharp

[<EntryPoint>]
let Main argv = 
    let host = new WebHostBuilder()
    let ihost = 
        host.UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseUrls(&quot;http://*:5000&quot;)
            .Build()
    ihost.Run()
    0

Startup.cs と Program.fs は定番の処理なので、Web APIのプロジェクトテンプレートを作ってしまえばよいと思います。
実際に手を入れていくのは、ValuesControllerクラスです。

ValuesController.cs

namespace ApiFSharp.Controllers 

open System
open System.Collections.Generic
open System.Linq
open System.Threading.Tasks
open Microsoft.AspNetCore.Mvc

[<Route(&quot;api/[controller]&quot;)>]
type ValuesController() =
    inherit Controller() 

    [<HttpGet>]
    member x.Get () = 
        [&quot;value1&quot;; &quot;value2&quot; ]
    [<HttpGet(&quot;{id}&quot;)>]
    member x.Get (id:int) = 
        &quot;value&quot;
    [<HttpPost>]
    member x.Post([<FromBody>] value:string) =
        ()
    [<HttpPut(&quot;{id}&quot;)>]
    member x.Put( id:int, [<FromBody>] value:string) =
        ()
    [<HttpDelete(&quot;{id}&quot;)>]
    member x.Delete( id:int ) =
        ()

データベースにアクセスしていないので、固定文字列しか返していないけど、SQLite を使ってデータアクセスを作れば結構使えるようになるかなと。

ビルドして実行する

リストアして、ビルドして、実行します。

dotnet restore
dotnet build
dotnet run

VSCodeで、Shift+Ctrl+B を押すとビルドができます。

実行する

PowerShell で dotnet run してブラウザで http://localhost:5000/api/values にアクセスします。

ちなみに Ubuntu 上の VSCode を使って F# プログラミングはこんな感じ。

固定の文字列を返すだけとか、計算するだけだとふつうに HttpListener を使って返すだけでも十分だろうという気もしますが、データベースアクセス付け加えたり、認証とかクッキーとかを使ってアクセス制限を考えたり、Controllerクラスのテストなどを考えると、ASP.NET Core を利用した Web API を F# で作るのもアリかな…という無理矢理な発想で :)

サンプルコード

サンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfwK1qbhxJOHVP0bxQ

カテゴリー: ASP.NET, F# | F#でASP.NET CoreのWeb APIを作ろう はコメントを受け付けていません

SQLite で LINQ を使う

と或るところで、さらっと答えてしまったら間違っていたので、仕切り直しがてらに進呈致します。どうやら、昔、どこかでオンメモリの設定で使っていてその知識がそのままだったようです。ファイルベースで動いていますね、SQLiteは。後述しますが、接続文字列のところで「DataSource=sample.db」のようにファイル名ではなくて「DataSource=:memory:」にするとオンメモリのDBとして動作します。

System.Data.SQLite を使う

SQLiteの本家サイトから落としてもいいのですが、手っ取り早く NuGet で落とします。

.NET Core 用には System.Data.SQLite.Core があるのと、Windows IoT Core のような UWP アプリで内部でデータベースを扱うのに SQLite 一択となるので、一度使ってみるといいかもしれません。NuGet には「Microsoft.Data.SQLite」もあるので、多少乱立ぎみなのかも。

SQLiteにテーブルを作る

実験用に users テーブルを作ります。SQL文は、一度 PupSQLite で作ったものから吐き出しています。

Pup’s Atelier-Software
https://www.eonet.ne.jp/~pup/software.html

オンメモリにする場合は、コネクションをクローズするたびに無くなってしまう(揮発性)なので、アプリが起動したときにコネクションを開いて、終わるときに閉じるという具合でしょう。

{
    string sql = @"
CREATE TABLE [users] (
[id] VARCHAR(256),
[username] VARCHAR(256) NOT NULL,
[email] VARCHAR(256),
[birthday] DATETIME,
[age] INTEGER,
[memo1] VARCHAR(256),
[memo2] VARCHAR(256),
[memo3] VARCHAR(256),
[memo4] VARCHAR(256),
[memo5] VARCHAR(256),
[memo6] VARCHAR(256),
[memo7] VARCHAR(256),
[memo8] VARCHAR(256),
[memo9] VARCHAR(256),
[memo10] VARCHAR(256),
PRIMARY KEY(id)
);";
    var cn = new SQLiteConnection("DataSource=" + db_file);
    var cmd = new SQLiteCommand(sql, cn);
    cn.Open();
    cmd.ExecuteNonQuery();
    cn.Close();
    textMsg.Text = "Users テーブルを作成しました";
}

Memo1 から Memo10 のデータは、ファイルを巨大にするためのダミーデータ用です。このテーブルで作ると10万件で、500MB ぐらいのファイルができあがります。

Userクラスを作る

LINQ で使えるように Entity クラスを作ります。
カラムの型は指定しなくても、うまくマッピングできています。Table属性とColumn属性のNameを指定すればokです。

using System.Data.Linq.Mapping;

[Table(Name ="users")]
class User
{
    [Column(Name ="id", IsPrimaryKey = true )]
    public string Id { get; set; }
    [Column(Name = "username", CanBeNull = false)]
    public string UserName { get; set; }
    [Column(Name = "email",CanBeNull = true)]
    public string Email { get; set; }
    [Column(Name = "birthday", CanBeNull = true)]
    public DateTime? Birthday { get; set; }
    [Column(Name = "age" ,CanBeNull = true)]
    public int? Age { get; set; }

    [Column(Name = "memo1")]
    public string Memo1 { get; set; }
    [Column(Name = "memo2")]
    public string Memo2 { get; set; }
    [Column(Name = "memo3")]
    public string Memo3 { get; set; }
    [Column(Name = "memo4")]
    public string Memo4 { get; set; }
    [Column(Name = "memo5")]
    public string Memo5 { get; set; }
    [Column(Name = "memo6")]
    public string Memo6 { get; set; }
    [Column(Name = "memo7")]
    public string Memo7 { get; set; }
    [Column(Name = "memo8")]
    public string Memo8 { get; set; }
    [Column(Name = "memo9")]
    public string Memo9 { get; set; }
    [Column(Name = "memo10")]
    public string Memo10 { get; set; }
}

検索用の LINQ

System.Data.Linqを参照設定しておいて、DataContextクラスを使います。テーブルを参照するときは、GetTableメソッドで指定のテーブルを取得。

private void clickCount(object sender, RoutedEventArgs e)
{
    var sw = new Stopwatch();
    sw.Start();
    var cn = new SQLiteConnection("DataSource=" + db_file);
    var context = new DataContext(cn);
    var users = context.GetTable<User>();
    int count = users.Count();

    textMsg.Text = $"{count} 件のデータがあります";
    textTime.Text = $"{sw.ElapsedMilliseconds} msec";
}

すると、LINQ が使えるようになるので、where文などを使って条件を指定できるようになります。

private void clickAge(object sender, RoutedEventArgs e)
{
    var sw = new Stopwatch();
    sw.Start();
    var cn = new SQLiteConnection("DataSource=" + db_file);
    var context = new DataContext(cn);
    var users = context.GetTable<User>();
    var q = from t in users
            where 40 <= t.Age && t.Age < 50
            select t;
    var count = q.Count();
    sw.Stop();
    textMsg.Text = $"{count} 件のデータがあります";
    textTime.Text = $"{sw.ElapsedMilliseconds} msec";
}

10万件のデータを検索したところ(インデックスなし)、300 mesc ちょっとで返って来ます。

メモリの具合はどうかというと、Working set の private が 30 MB 程度。このとき sample.db ファイルの大きさは 500MB 程度あるので、ファイルアクセスをしています。年齢で検索するために clickAge を呼び出した瞬間に HDD アクセスが大量発生してます。

となると検索スピードは HDD/SSD のスピードに依るということですね。

データ作成

データ作成をする INSERT は意外と遅いです。ロジックが多少複雑だというのもあるんでしょうが、1000件作るのに7秒程度かかります。オンメモリにすると 300msec 程度なので insert スピードが 20倍ぐらい違います。

private void clickCreateData(object sender, RoutedEventArgs e)
{
    var cn = new SQLiteConnection("DataSource=" + db_file); //  ";SyncMode=off;JournalMode=Memory");
    var context = new DataContext(cn);
    var users = context.GetTable<User>();
    _rnd = new Random();
    int max = int.Parse(textSize.Text);
    var sw = new Stopwatch();
    sw.Start();
    for ( int i=1; i<=max; i++ )
    {
        users.InsertOnSubmit(makeUser());
        if ( i % 100 == 0 )
        {
            context.SubmitChanges();
            Debug.WriteLine($"{i} 件 挿入...");
        }
    }
    context.SubmitChanges();
    sw.Stop();
    textMsg.Text = $"{max} 件のデータを挿入しました";
    textTime.Text = $"{sw.ElapsedMilliseconds} msec";
}

private User makeUser()
{
    var user = new User();
    user.Id = Guid.NewGuid().ToString("D");
    user.UserName = createName();
    user.Email = createEmail(user.UserName);
    user.Birthday = null;
    user.Age = _rnd.Next(10, 100);
    user.Memo1 = createMemo();
    user.Memo2 = createMemo();
    user.Memo3 = createMemo();
    user.Memo4 = createMemo();
    user.Memo5 = createMemo();
    user.Memo6 = createMemo();
    user.Memo7 = createMemo();
    user.Memo8 = createMemo();
    user.Memo9 = createMemo();
    user.Memo10 = createMemo();

    return user;
}
private string createName()
{
    // xxxxxx xxxxxx
    var name = "Aaaaaa Aaaaaa";
    var ch = name.ToCharArray();
    for (int i = 0; i < ch.Length; i++) {
        if (ch[i] == ' ') continue;
        ch[i] = (char)(ch[i] + _rnd.Next(26));
    }
    return new string(ch);
}
private string createEmail( string username )
{
    // xxxxxx xxxxxx
    var name = username.Split(' ')[0];
    return name + "@mail.com";
}
private string createMemo(int size = 256)
{
    var ch = new char[size];
    for (int i = 0; i < size; i++)
    {
        ch[i] = (char)('A' + _rnd.Next(26));
    }
    return new string(ch);
}

機会を作って、.NET Core のほうの SQLite も試してみるということで。

サンプルコード

サンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfsUYttAgWYXnhiogw

参考先

C# で SQLite を便利に使うサンプルコード(LINQ to SQLite) – 翔星 Be ランド日記
http://shinta0806be.ldblog.jp/archives/9084539.html

In-Memory Databases
https://www.sqlite.org/inmemorydb.html

 

カテゴリー: SQLite | 1件のコメント

ASP.NET Core MVC の Web API で JSON 形式のデータを扱う

Web API の JSON 形式に関しては、

Building Your First Web API with ASP.NET Core MVC and Visual Studio ? ASP.NET documentation
https://docs.asp.net/en/latest/tutorials/first-web-api.html

に詳しい解説があります。
が、クライアント側が書いていないので、先の XML 形式と同じように WFP アプリでクライアントを書いていきます。

送受信の形式

  • WPF アプリで JSON 形式で送信
  • ASP.NET Core Web API で JSON 形式で返信

することを考える。データはいちいち JSON 形式に直すのは面倒なので、C# のクラスから Newtonsoft.Json.JsonSerializer を使ってシリアライズ/デシリアライズをする。

Web API 側の設定

XML 形式の場合は Formatters を追加したが、JSON の場合はもともとロードされているので不要。
JsonOutputFormatter と JsonInputFormatter が初期値で使われている。

Web API の PeopleController クラスを作る

Modelクラスである Person クラスを作っておく。

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

JsonSerializer は、IEnumerable<Person> もでシリアライズしてくれるので、XML 形式のような People クラスは不要で、そのまま使える。

コントローラーを作るときに「Entitiy Frameworkを使用したアクションがあるAPIコントローラー」を選べば、CURD機能のAPIが、ずらっと出力される。

このまま動くので改変しなくてよい。

[Produces("application/json")]
[Route("api/People")]
public class PeopleController : Controller
{
    private readonly ApplicationDbContext _context;

    public PeopleController(ApplicationDbContext context)
    {
        _context = context;
    }

    // GET: api/People
    [HttpGet]
    public IEnumerable<Person> GetPerson()
    {
        return _context.Person;
    }

    // GET: api/People/5
    [HttpGet("{id}")]
    public async Task<IActionResult> GetPerson([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id);

        if (person == null)
        {
            return NotFound();
        }

        return Ok(person);
    }

    // PUT: api/People/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutPerson([FromRoute] int id, [FromBody] Person person)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != person.Id)
        {
            return BadRequest();
        }

        _context.Entry(person).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!PersonExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        // return NoContent();
        return await GetPerson(person.Id);
    }

    // POST: api/People
    [HttpPost]
    public async Task<IActionResult> PostPerson([FromBody] Person person)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Person.Add(person);
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException)
        {
            if (PersonExists(person.Id))
            {
                return new StatusCodeResult(StatusCodes.Status409Conflict);
            }
            else
            {
                throw;
            }
        }

        return CreatedAtAction("GetPerson", new { id = person.Id }, person);
    }

    // DELETE: api/People/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeletePerson([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        Person person = await _context.Person.SingleOrDefaultAsync(m => m.Id == id);
        if (person == null)
        {
            return NotFound();
        }

        _context.Person.Remove(person);
        await _context.SaveChangesAsync();

        return Ok(person);
    }

    private bool PersonExists(int id)
    {
        return _context.Person.Any(e => e.Id == id);
    }
    /// <summary>
    /// 受け口を POST に変換する
    /// </summary>

    [HttpPost("{id}")]
    [Route("Edit/{id}")]
    public async Task<IActionResult> Edit([FromRoute] int id, [FromBody] Person person)
    {
        return await PutPerson(id, person);
    }
    [HttpPost]
    [Route("Create")]
    public async Task<IActionResult> Create([FromBody] Person person)
    {
        return await PostPerson(person);
    }
}

クライアントからの表示の都合上 PutPerson の戻り値を変えている。
あと、POST 形式だけで通るように、Edit と Create を追加している。

WPF クライアントを作る

XML 形式のときと同じように、Person クラスだけを作る。
JsonSerializer が使えるように、Newtonsoft.Json を NuGet で参照設定させておく。

namespace ClientJson
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private async void clickGet(object sender, RoutedEventArgs e)
        {
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var res = await hc.GetAsync("http://localhost:5000/api/people");
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var js = new Newtonsoft.Json.JsonSerializer();
            var jr = new Newtonsoft.Json.JsonTextReader( new System.IO.StringReader(str));
            var items = js.Deserialize<IEnumerable<Person>>(jr);
            textPerson.Text = "";
            foreach (var item in items)
            {
                textPerson.Text += $"{item.Id} {item.Name} {item.Age} n";
            }
        }

        private async void clickGetById(object sender, RoutedEventArgs e)
        {
            int id = 2;
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var res = await hc.GetAsync($"http://localhost:5000/api/people/{id}");
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var js = new Newtonsoft.Json.JsonSerializer();
            var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str));
            var item = js.Deserialize<Person>(jr);
            textPerson.Text = $"{item.Id} {item.Name} {item.Age}";
        }

        private async void clickPutById(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 2, Name = "update person", Age = 99 };
            var js = new Newtonsoft.Json.JsonSerializer();
            var sw = new System.IO.StringWriter();
            js.Serialize(sw, person);
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var json = sw.ToString();
            var cont = new StringContent(json, Encoding.UTF8, "application/json");
            var res = await hc.PutAsync($"http://localhost:5000/api/people/{person.Id}", cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str));
            var item = js.Deserialize<Person>(jr);
            textPerson.Text = $"{item.Id} {item.Name} {item.Age}";
        }

        private async void clickPost(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 0, Name = "new person", Age = 88 };
            var js = new Newtonsoft.Json.JsonSerializer();
            var sw = new System.IO.StringWriter();
            js.Serialize(sw, person);
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var json = sw.ToString();
            var cont = new StringContent(json, Encoding.UTF8, "application/json");
            var res = await hc.PostAsync($"http://localhost:5000/api/people", cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str));
            var item = js.Deserialize<Person>(jr);
            textPerson.Text = $"{item.Id} {item.Name} {item.Age}";
        }

        private void clickDeleteById(object sender, RoutedEventArgs e)
        {
        }

        private async void clickCreate(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 0, Name = "new person", Age = 88 };
            var js = new Newtonsoft.Json.JsonSerializer();
            var sw = new System.IO.StringWriter();
            js.Serialize(sw, person);
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var json = sw.ToString();
            var cont = new StringContent(json, Encoding.UTF8, "application/json");
            var res = await hc.PostAsync($"http://localhost:5000/api/people/Create", cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str));
            var item = js.Deserialize<Person>(jr);
            textPerson.Text = $"{item.Id} {item.Name} {item.Age}";
        }

        private async void clickEdit(object sender, RoutedEventArgs e)
        {
            var person = new Person() { Id = 2, Name = "edit person", Age = 99 };
            var js = new Newtonsoft.Json.JsonSerializer();
            var sw = new System.IO.StringWriter();
            js.Serialize(sw, person);
            var hc = new HttpClient();
            // hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var json = sw.ToString();
            var cont = new StringContent(json, Encoding.UTF8, "application/json");
            var res = await hc.PostAsync($"http://localhost:5000/api/people/Edit/{person.Id}", cont);
            var str = await res.Content.ReadAsStringAsync();
            textXml.Text = str;

            var jr = new Newtonsoft.Json.JsonTextReader(new System.IO.StringReader(str));
            var item = js.Deserialize<Person>(jr);
            textPerson.Text = $"{item.Id} {item.Name} {item.Age}";
        }
    }
}
namespace SampleWebApiXml.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}
  • Web APIの戻り値形式が、デフォルトでJSONなので、 hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”)); は、設定しなくてもよい。
  • Person クラスと JSON 形式は、JsonSerializer.Serialize と Deserializeを使えばよい。
  • これも、日本語を通すためにはきちんと UTF8 エンコードが必要かも。

でもって、うまく動くと dotnet run でサーバーを起動、WPF クライアントから JSON 形式で送受信ができるようになる。ここまで、できるようになれば、クライアントを Javascript や Ruby に切り替えたり、サーバー側を CakePHP で切り替えて相互に動かせるようになる。

サンプルコード

動作できるサンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgfsRYiYpmZzonGIBZw

カテゴリー: ASP.NET | ASP.NET Core MVC の Web API で JSON 形式のデータを扱う はコメントを受け付けていません