🐥note.

小鳥とMicrosoft <3 なエンジニアの技術Blog📚

.NET CoreのDiagnostics CLI Toolを触ってみた

2019年10月のMicorosoft Docsの更新内容を見ていて.NET Core Diagnostics CLI Toolというものがあることに気が付きました。
それぞれdotnet-counters, dotnet-dump, dotnet-traceなるものっぽいです。

それぞれちょっとずつ試してみました。

dotnet-counters

.NET CoreなアプリのCPU使用率やヒープサイズ, 世代毎GCカウントなどをCLI上でモニタリングするPerformance Counterのようです。

docs.microsoft.com

下記コマンドでインストールできます。

dotnet tool install --global dotnet-counters

使用する際は監視対象アプリのPIDが必要です。
dotnet-countersには.NET Coreで動いてるアプリのPIDを調べる機能が最初から備わっているので、そのコマンドでPIDを調べます。

Blazorを動かしている状態でdotnet-counters psしてみます。

f:id:piyo_esq:20191107070927p:plain
dotnet-counter ps

PIDは12960ですね。
dotnet-countersでモニタリングしてみましょう。

dotnet-counters monitor -p {PID}

こんな感じの画面になりました。

f:id:piyo_esq:20191107071002p:plain
dotnet-counter monitor

取得するDLLを絞ったり、データ項目を限定することもできるようです。

dotnet-counters monitor -p {PID} System.Runtime[cpu-usage,gc-heap-size]

f:id:piyo_esq:20191107071024p:plain
dotnet-counter monitor結果

ほーん。

dotnet-dump

所謂メモリダンプでしょうか。

docs.microsoft.com

いくつか注意する点があるようです。Microsoft Docsから引用します。

dotnet-dump グローバル ツールを使用すると、Linux の lldb などの任意のネイティブ デバッガーを使用せずに、Windows と Linux のダンプを収集して分析できます。 このツールは、lldb が完全に動作しない Alpine Linux などのプラットフォームで重要です。 dotnet-dump ツールでは、SOS コマンドを実行してクラッシュとガベージ コレクター (GC) を分析できますが、これはネイティブのデバッガーではないため、ネイティブ スタック フレームの表示などの操作はサポートされていません。

下記コマンドでインストールできます。
※Macは非対応の機能のようです。

dotnet tool install -g dotnet-dump

さっそくDump取ってみましょう。

dotnet-dump collect -p {PID}

f:id:piyo_esq:20191107071106p:plain
dotnet-dumo collect

Dumpを取り終わった後はdotnet-dump analyzeでDump結果を分析できるようです。

dotnet-dump analyze {dumpファイル名}

f:id:piyo_esq:20191107071128p:plain
dotnet-dump analyze

対話型セッションに移りました。
clrstackでアセンブリに関すマネージド コードのみのスタック トレースを表示します。

f:id:piyo_esq:20191107071150p:plain
dotnet-dump analyze clrstack

ほーん。

dotnet-trace

Traceログを採取できるようです。

docs.microsoft.com

下記コマンドでインストールできます。

dotnet tool install --global dotnet-trace

Traceログを採取します。

dotnet-trace collect -p {PID}

f:id:piyo_esq:20191107071219p:plain
dotnet-trace collect

デフォルトだとCPU-Samplingになるっぽいですね。
他にもgc-verboseやgc-collectもあるっぽい。

f:id:piyo_esq:20191107071239p:plain
dotnet-trace list-profiles

出力されたtrace.nettraceをPerfViewで見てみましょう。

f:id:piyo_esq:20191107071309p:plain
PerfView Top画面

f:id:piyo_esq:20191107071336p:plain
Event Statics

ちゃんと開けました。

おわり

いずれも.NET Core 3.0以降を対象としたツールだそうです。
Macではdotnet-dumpが使えない点にも注意ですね。

2019年10月の.NET/ASP.NET Documentation Updateについて

Redditに2019年10月の.NET / ASP.NET のドキュメンテーションの更新状況について投稿がありました。

www.reddit.com

なお、先月の更新状況は以下の通りです。

blog.piyosi.com

2019年10月の変更状況

2019/10/01 ~ 2019/11/05の間に

  • .NETには575件コミットされた

  • ドキュメントリポジトリの4,599個のファイルが変更された

  • APIドキュメントリポジトリは124回のPRを受けた

  • ASP.NETのリポジトリには288件コミットされた

  • ドキュメントリポジトリの 1,317個のファイルが変更された

  • Nugetは32件コミットされた

  • ドキュメントリポジトリの26個のファイルが変更された

今月のテーマ

今月のテーマとしては以下の点が挙げられるそうです。

  • .NET CoreのCLIの更新
  • docsfx.jsonの改修
  • C#8.0アップデート
  • C#8.0仕様の更新

今月のみんなが読みたくなる記事📢

特になし

今月の重要記事💥

今月の重要記事💥はI/O pipelinesのみのようです。

今月の新しいページ✨

あとは言語リファレンスやVB, ML.NET, Apache Spark, gRPCあたりに新しい記事がきてるっぽいですね。

おわり

dotnet-counters, dotnet-dump, dotnet-trace知りませんでした!
なんだこれは!

ASP.NET Core Update in .NET Core 3.1 Preview2の新機能について

Microsoft Igniteで盛り上がるIT界の裏では.NET Core 3.1 Preview2がリリースされました。

devblogs.microsoft.com

それに合わせてASP.NET CoreとEntityFramework CoreもPreviewが公開されています。

devblogs.microsoft.com

本エントリではASP.NET Coreに追加された新機能について個人的にまとめます。

新機能について

上記公式Blogから抜粋

  • New component tag helper
  • Prevent default actions for events in Blazor apps
  • Stop event propagation in Blazor apps
  • Validation of nested models in Blazor forms
  • Detailed errors during Blazor app development

詳しく見ていきます。

New component tag helper

以前はRenderComponentAsyncHTMLヘルパーを使用して、ビューまたはページからコンポーネントをレンダリングしていました。

@(await Html.RenderComponentAsync<Counter>(RenderMode.ServerPrerendered, new { IncrementAmount = 10 }))

新しいコンポーネントタグヘルパーは以下の通り。

<component type="typeof(Counter)" render-mode="ServerPrerendered" param-IncrementAmount="10" />

レンダリングするComponentのTypeと、レンダーモードを指定します。 また、Componentに対するParameterをparam-句で指定できるようになりました。

それに伴いBlazorのTemplateで作成される_Host.cshtmlの内容が変化しています。

<!-- 途中省略 -->
<body>
    <app>
-        @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
+        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

componentを使うよう変化していますね。

Blazorに限ると_Host.cshtml以外から使うことってあるのでしょうか...?
よく分かりません。

Prevent default actions for events in Blazor apps

@oneventname:preventDefault ディレクティブ属性を使用して、BlazorアプリのEventのデフォルトアクションを防止できるようになりました。

私は今まではRazor内に普通にJavaScript関数として記述していました。こんな感じです。
ondragoverは@なしなので、JavaScriptの関数です。

<div ondragover="event.preventDefault()" @ondrop="@OnDrop">
    ...
</div>

今回の変更で@つきのディレクティブ属性になったので、書き方に統一感が出ましたね。
変更後はこんな感じに書けるようです。

<div @ondragover:preventDefault @ondrop="@OnDrop">
    ...
</div>

Stop event propagation in Blazor apps

@oneventname:stopPropagationディレクティブ属性を使用して、Blazorアプリでイベントの伝播を停止します。
次の例では、チェックボックスをオンにすると、子divからのクリックイベントが親に伝播しなくなります。

<input @bind="stopPropagation" type="checkbox" />
<div @onclick="OnClickParentDiv">
    Parent div
    <div @onclick="OnClickChildDiv" @onclick:stopPropagation="stopPropagation">
        Child div
    </div>
</div>

<button @onclick="OnClick">Click me!</button>

@code {
    bool stopPropagation;

    void OnClickParentDiv() => Console.WriteLine("Parent div clicked.");
    void OnClickChildDiv() => Console.WriteLine("Child div clicked.");
}

これ地味にめっちゃありがたいやつでは?

Validation of nested models in Blazor forms

検証対象のModel内に存在するクラスのプロパティの検証ができるようになった感じでしょうか?

探したところ、こちらにサンプルが上がっていました。

github.com

MergeされたPRはこちらでしょうか?

github.com

サンプルを見た感じ、ObjectGraphDataAnnotationsValidatorとValidateComplexType属性がキモなのかな?

Person.cs

using System.ComponentModel.DataAnnotations;

namespace Validation.Data
{
    public class Person
    {
        [Required]
        public string Name { get; set; }

        [ValidateComplexType]
        public Address Address { get; set; } = new Address();

        [ValidateComplexType]
        public PhoneNumber[] PhoneNumbers { get; set; } = new[]
        {
            new PhoneNumber(),
            new PhoneNumber(),
        };
    }
}

ValidateComplexType属性を付与された側のClass(AddressとPhoneNumberクラス)は特に意識する必要はないようです。

PhoneNumber.cs

using System.ComponentModel.DataAnnotations;

namespace Validation.Data
{
    public class PhoneNumber
    {
        public string Description { get; set; }

        [Required]
        public string Number { get; set; }
    }
}

使用するにはMicrosoft.AspNetCore.Blazor.DataAnnotations.Validationというパッケージが必要のようです。
インストール時の注意としては--version指定をしなければインストールできませんでした。

Nugetはこちら

www.nuget.org

dotnet add package Microsoft.AspNetCore.Blazor.DataAnnotations.Validation --version 3.1.0-preview2.19528.8

Microsoft.AspNetCore.Blazor.DataAnnotations.Validationは実験的な位置づけであり今のところ.NET Core 3.1に同封する計画はないそうです。

Detailed errors during Blazor app development

TagHelperの件も相まってTemplateが更新されました。

github.com

...
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

エラー発生時の画面に関するブロックが追加されていますね!

どんな感じにメッセージが出力されるのか、Develop環境で例外を投げてみます。

画面下部の黄色い帯がエラーメッセージ部分です。
メッセージの通り、ChromeのDeveloper Toolには例外の内容が吐かれています。

f:id:piyo_esq:20191105201542p:plain
Develope環境で例外をthrow

続いてProduction環境です。

f:id:piyo_esq:20191105201621p:plain
Production環境で例外をthrow

Developer Toolで表示されているメッセージの通りappsetting.jsonでDetailedErrorsをtrueに設定してみます。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
+ "DetailedErrors": true
}

または、Statrup.csを以下のように変更します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
-   services.AddServerSideBlazor();
+   services.AddServerSideBlazor().AddCircuitOptions(option =>
+   {
+       option.DetailedErrors = true;
+   });
    services.AddSingleton<WeatherForecastService>();
}

例外を投げます。
Production環境なのに例外を吐くやばい感じなりました。

f:id:piyo_esq:20191105201653p:plain
Production環境で例外が見れちゃう

おわり

Templateがアプデされてエラー画面が簡単に書けるようになったの非常に良いですね。

@{eventName}:stopPropagationも何かと便利ですし、何よりValidateComplexTypeによる検証が楽しみですね。

自作RazorClassLibraryの静的ファイルのロードができずに困った話

BlazorのComponentを別プロジェクト(RazorClassLibrary)へ切り分けた際、
Blazorから別プロジェクトの静的リソースへアクセスすることが出来ずにハマったので、それの件について記録します。

SolutionとProjectの作成

以下のコマンドでBlazorAppと、Component役のMyComponentを作成します。
※Component役プロジェクトはrazorclasslibで生成します。

dotnet new sln -o BlazorComponentSample
cd BlazorComponentSample
dotnet new blazorserver -o BlazorApp
dotnet new razorclasslib -o MyComponent
dotnet add .\BlazorApp\BlazorApp.csproj reference .\MyComponent\MyComponent.csproj
dotnet sln .\BlazorComponentSample.sln add BlazorApp MyComponent

MyComponentの作成

MyComponent.csprojのTargetFrameworkバージョンを上げておきます。

<Project Sdk="Microsoft.NET.Sdk.Razor">

  <PropertyGroup>
-   <TargetFramework>netstandard2.0</TargetFramework>
+   <TargetFramework>netstandard2.1</TargetFramework>
    <RazorLangVersion>3.0</RazorLangVersion>
  </PropertyGroup>

</Project>

MyComponentではCSSフレームワークにBulmaを使用することにします。 Bulmaはnpmで入れたいので、MyComponentフォルダ以下にpackage.jsonを作成します。

package.json

{
  "version": "1.0.0",
  "name": "blazor",
  "private": true
}

以下のコマンドでBulmaをインストールします。
MyComponentフォルダ以下にnode_moduleフォルダが生成されます。

npm install --save-dev Bulma

ビルド時にnode_moduleフォルダからwwwrootフォルダへBulmaのcssをコピーする設定を行います。
以下のコマンドで、BuildBundlerMinifierをインストールします。

dotnet add package BuildBundlerMinifier

MyComponentフォルダにbundleconfig.jsonを作成します。

[
  {
    "outputFileName": "wwwroot/lib/bulma/css/bulma.min.css",
    "inputFiles": ["node_modules/bulma/css/bulma.min.css"],
    "minify": {
      "enabled": false,
      "renameLocals": true
    }
  }
]

これでビルド時にファイルのバンドルが行われるようになりました。
試しにdotnet buildでビルドしてみましょう。wwwroot以下にlib/bulma/css/bulma.min.cssがあれば成功です。

続いてComponent.razorの編集を行います。

呼び元からName, SNSId, ContentをParameterとして受け取り、表示するだけです。

<div class="box">
    <article class="media">
        <div class="media-left">
            <figure class="image is-64x64">
                <img src="bird.png" alt="Image">
            </figure>
        </div>
        <div class="media-content">
            <div class="content">
                <p>
                    <strong>@Name</strong> <small>@SNSId</small>
                    <br>
                    @Content
                </p>
            </div>
        </div>
    </article>
</div>

@code {
    [Parameter]
    public string Name { get; set; }
    [Parameter]
    public string SNSId { get; set; }
    [Parameter]
    public string Content { get; set; }
}

ここまで書いて、ようやくハッと気が付きました。
このComponent役を呼ぶと、静的ファイルのパスってどこ起点になるんでしょうか?

調べた結果、Microsoft Docs先生に答えが書いてありました。

docs.microsoft.com

RCL のwwwrootフォルダーに含まれるファイルは、使用中のアプリにプレフィックス content/{LIBRARY NAME}/で公開されます。 たとえば、という名前のライブラリを使用すると、 content/Razor.Class.Lib/の静的コンテンツへのパスが生成されます。

つまりMyComponent\wwwroot\bird.pngにあるファイルは_content/MyComponent/bird.pngで参照するのが正しいようです。
<img>のsrcを_content/MyComponent/bird.pngに修正します。

以上でMyComponent側の作業は完了です。

BlaozrApp側の作成

Index.razorにMyComponentで作成したコンポーネントを表示します。

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<MyComponent.Component1 Name="piyosi" SNSId="piyo_esq" Content="👈このかわいいトリちゃんは実家のセキセイインコです。以後よろしくお願い致します。"></MyComponent.Component1>

MyComponentで使用しているBulmaのCSSを_Host.cshtmlでロードします。
※bootstrapとbulmaが共存している件は、サンプルコードなので無視します。

@page "/"
@namespace BlazorApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BlazorApp</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
 +  <link href="_content/MyComponent/lib/bulma/css/bulma.min.css" rel="stylesheet" />
</head>
<body>
    <app>
        @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
    </app>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

動かしてみます

cd BlazorApp
dotnet run

f:id:piyo_esq:20191103070751p:plain
実行結果

デフォのBootstrapも入ってるの分かりにくいですが、ちゃんとBulmaが効いています。

おわり

自分で作ったRazorClassLibraryや静的ファイルを使用するライブラリを使用する際は_content/{LibraryName}/{静的ファイルのパス}に注意しないといけないの、覚えました。

もっと画像の表示領域を大きくしておけばよかったな、と反省しています。

Azure DevOpsでSprintの名前を変更する

Azure DevOpsでSprintsの名前を間違えてしまいました。

f:id:piyo_esq:20191103062513p:plain

名前を変更するには画面左下のProject settings -> Boards -> Project configurationから変更することができるようです。

f:id:piyo_esq:20191103062612p:plain

おわり

ぐぐらないと見つけられませんでした...。