ツナ缶雑記

ぐうたらSEのブログです。主にマイクロソフト系技術を中心に扱います。

AMD プロセッサでも Hyper-V を入れ子にできるようになる話

f:id:masatsuna:20211019003325p:plain

以前こんな記事を書きました。

tsuna-can.hateblo.jp

今まで Hyper-V仮想マシン内にさらに Hyper-V を立ち上げたり、 Docker を立ち上げたり、ということが AMD プロセッサではできませんでした。 しかし、ようやくと言うか、ついにと言うか、 AMD プロセッサでも Hyper-V入れ子ができるようになるそうです。

念のため歴史を紐解く

もともと AMD プロセッサでは Hyper-V入れ子はできなかったのですが、以下のブログポストにもある通り、 Windows 10 の Insider 向けにこの制限が解除される動きがありました*1

https://techcommunity.microsoft.com/t5/virtualization/amd-nested-virtualization-support/ba-p/1434841

長らくこの機能は Insider 向けにのみ提供されてきましたが、 Windows 11 と Windows Server 2022 から、ついに一般向けにもリリースされるようです。

詳細が知りたい場合は英語版のページを見る

この Hyper-V入れ子については、まだ日本語ドキュメントが整備されていません。 詳細が知りたい方は、以下の英語版ページを参照してください。

docs.microsoft.com

AMD プロセッサの場合、少々条件がきつめです。 OS の最新化が避けられないようです。 記載を抜粋しておきます。

AMD EPYC/Ryzen processor or later

Note

The guest can be any Windows supported guest operating system. Newer Windows operating systems may support enlightenments that improve performance.

もうしばらくは様子見

今回は AMD プロセッサでも Hyper-V入れ子ができるようになる話をご紹介しました。 ホスト OS を Windows 11 にあげないといけないので、2021 年 10 月現在は少々ハードルが高い条件に見えます。 Ryzen のお高めのプロセッサだと、 Windows 11 が性能をフルに発揮できない問題が起きています。



これが解消しない限り、メインマシンの OS 更新はできないです。。。 条件が整ってからじゃないとお試しできないのが悲しいところです。

早いところ安心して Windows 11 にアップデートできるようになると良いですね。 もう少しでパッチが出るという話もあるので、そこに期待しましょう。 そしてこの機能には個人的に非常に期待しているので、早く安心して使える環境になってほしいですね。

*1:この話があったので、そのうち AMD プロセッサでも Hyper-V入れ子ができるんだろうとふんで、私は Ryzen にしたのもあります

Windows 11 Pro をディスクからインストールしてみた

f:id:masatsuna:20211006013149p:plain

2021 年 10 月 5 日、 Windows 11 がリリースされました。 私はチキンなので、実利用環境へのインストールはいったんやめて、 Windows 10 Pro 上の Hyper-V に ISO イメージからインストールしてみようと思います。 今回は日本語ローカライズ済みの Windows 11 Pro をインストールします。

環境

ホストマシンは以下の通りです。

仮想マシンを作る

普通に Hyper-V 上で仮想ハードディスク、仮想マシンを作成します。 設定は以下の通りです。

仮想ハードディスク

項目 設定値
ディスクのフォーマット VHDX
ディスクの種類 容量可変
ディスクの構成 新しい仮想ディスク(127GB)

仮想マシン

項目 設定値
世代 第 2 世代
起動メモリ 8GB
動的メモリ 無効
ネットワーク 有効
仮想プロセッサ 8個
SCSIコントローラー [DVD ドライブ] を追加
ブート順 DVD ドライブを最優先に設定

Windows 11 のインストール(TPM 無効の場合)

何かと話題に上っていた TPM ですが、実際に無効状態でインストールしてみて、インストーラーの挙動を確認してみます。 ホストマシンの Hyper-V マネージャーで、 Windows 11 をインストールする前の仮想マシンを電源オフにして、 TPM の設定を確認します。 以下の通り、 [トラステッド プラットフォーム モジュールを有効にする] のチェックを外します。

f:id:masatsuna:20211005221221p:plain

この状態で Windows 11 の ISO イメージを仮想マシンに設定し、ブートしてみます。

f:id:masatsuna:20211005221545p:plain

普通にインストーラーが立ち上がりました。 この部分は Windows 10 のインストーラーと変わらないみたいですね。

そのままインストールを先に進めようとすると。。。

f:id:masatsuna:20211005221626p:plain

TPM が使えないとインストールできないよう制御されていることが確認できました。 ただ、インストールできない理由が何かは教えてもらえません。 ちょっと不親切ですが、こんなことやるのわかっている人だけだと思うので、気にしないでおきます。 たぶん多くの人は Windows 10 からのアップグレードでしょうし、そうであればPC 正常性チェック アプリを使って詳細情報を確認できます。

新しい Windows 11 OS へのアップグレード | Microsoft

Windows 11 のインストール(TPM 有効の場合)

さて、今度はちゃんと TPM を有効にして、 Windows 11 Pro をインストールしてみます。 一旦仮想マシンをシャットダウンして、仮想マシンの設定を変更します。 先ほどオフにしていた [トラステッド プラットフォーム モジュールを有効にする] のチェックを入れておきます。

f:id:masatsuna:20211005221900p:plain

この辺りは以下の記事でも記載しているので参考にしてください。

tsuna-can.hateblo.jp

また物理マシンで挑戦する場合は以下の記事も参考にしてください。 MSI の記事は AMDASUS の記事は Intelチップセットの設定手順です。

tsuna-can.hateblo.jp

tsuna-can.hateblo.jp

プロダクトキーを適切に入力してインストールを始めてみます。

f:id:masatsuna:20211005222247p:plain

ここも Windows 10 のインストーラーと同じような感じですね。

しばらく待っていると、画面が切り替わります。 国を選択します。

f:id:masatsuna:20211005222343p:plain

続いてキーボードの設定を確認します。

f:id:masatsuna:20211005222454p:plain

画像にはありませんが、 2 つ目のキーボード設定はスキップしました。 するとアップデートの確認が走ります。

f:id:masatsuna:20211005222520p:plain

続いて PC の名前設定画面に移ります。 適当な名前を付けて次に進みます。

f:id:masatsuna:20211005222620p:plain

ここで一度再起動が走りました。 自動的に再起動後、用途の設定に移ります。 ドメインに参加するかどうかで選択肢を切り替えます。 個人で利用するならほとんどの場合 [個人用に設定] を選択しましょう。 企業などドメイン環境下で使う場合は [職場または学校用に設定する] を選択します。 本稿では [個人用に設定] の方で進みます。

f:id:masatsuna:20211005222651p:plain

続いてログイン用の MS アカウントの設定です。 この辺は適当にやってください。

f:id:masatsuna:20211005222852p:plain

ローカルアカウントでログインしたい場合は、 [サインイン オプション] のリンクを押下して、 [オフライン アカウント] を選択すると、ローカルアカウントを作成できるようです。 なおローカルアカウントは Windows 11 Home では使えないようです。 Pro にアップデートするか、 MS アカウントを取得しましょう。

続いて PIN の作成を求められます。 適当に作っておきましょう。

f:id:masatsuna:20211005223034p:plain

ただし Hyper-V で使う場合、 PIN を作ってしまうと拡張デスクトップが使えなくなります。 インストール時は PIN の作成が強制みたいなので、一旦拡張デスクトップを無効にしてログインしましょう。 その後、 Windows Hello を無効に設定してから拡張デスクトップを再度有効化するのがよさそうです。 詳細は以下の記事を参照してください。

tsuna-can.hateblo.jp

OneDrive を経由して、過去に使っていたマシンから設定やらを引き継げるみたいです。 今回は余計なお世話なので [新しいデバイスとして設定する] を選択して次に進みます。

f:id:masatsuna:20211005223631p:plain

続いて各種トラッキング系の設定です。 お好きに設定して進んでください。

f:id:masatsuna:20211005224901p:plain

次もトラッキング系の設定です。 お好きに設定して進んでください。

f:id:masatsuna:20211005224946p:plain

続いて OneDrive の使い方に関する設定です。 この設定、実は OneDrive を使うかどうかの設定ではありません。 どちらを選択しても OneDrive はインストールされます。 [OneDrive でファイルのバックアップを行う] を選択すると、デスクトップやドキュメント、ピクチャに保存したものが、勝手に OneDrive にアップロードされます*1。 複数のマシンに同じ MS アカウントでログインする人で、ドキュメント類を PC 間で共有したいニーズがある場合のみ上を選択しましょう。 私はバックアップ・共有したいものだけ選択してアップロードしたいので、 [ファイルのバックアップを行わない] を選択して次に行きます。

f:id:masatsuna:20211005225647p:plain

これでインストール時の設定はすべておしまいで、インストールが終わるのを待つだけです。

f:id:masatsuna:20211005225743p:plain

無事に起動しました。

f:id:masatsuna:20211005230600p:plain

一応バージョンを確認したところ、 22000.194 でした。 前回 Beta チャネルに配信されたものと同等品のようです。

*1:本気でこの設定は余計なお世話だと思います。デフォルトはバックアップなしにしてほしいです。

Hyper-V で画面が表示されない場合の確認項目(Windows 11)

f:id:masatsuna:20211006011733p:plain

以前 Windows 10 の場合の設定方法については記載しています。 今回は Windows 11 の設定箇所を備忘録かねて書いておきます。

tsuna-can.hateblo.jp

拡張セッションを使いたい場合は Windows Hello を無効にする

拡張セッションを使うと、ホスト OS のドライブにアクセスできるようになるので、非常に便利です。 拡張セッションを使いたいのであれば、ゲスト OS 側の設定で、 Windows Hello を無効にしましょう。 PIN を有効にすると Windows Hello が有効になります。 この状態だと拡張セッションが使えません。

Windows 11 をディスクからインストールすると、 PIN の設定が強制されるようです。 Hyper-V 上で実行する場合は、最初にこの設定を変更しないとうまく画面が表示できなくなります。

スタートボタンを右クリックして [設定] を選択します。

f:id:masatsuna:20211006011258p:plain

左側メニューから [アカウント] を選択して [サインイン オプション] を選択します。

f:id:masatsuna:20211006011347p:plain

[セキュリティ向上のため、このデバイスでは Microsoft アカウント用に Windows Hello サインインのみを許可する] のトグルをオフにします。

f:id:masatsuna:20211006011407p:plain

これで拡張セッションが利用できるようになります。

タブの色分け表示機能がよい(Visual Studio 2022 Preview 4)

f:id:masatsuna:20210918143154p:plain

どんな機能か

Visual Studio 2022 Preview 4 でタブの色分け機能が追加されています。 こんな感じで、プロジェクトごとにタブを色分けしてくれて、どのファイルがどのプロジェクトに所属するかわかりやすくなっています。

f:id:masatsuna:20210918122222p:plain

非常にわかりやすくてよいですね。 個人的にとても気に入りました。

なお従来型の水平タブでも同じような色分け表示はできます。

f:id:masatsuna:20210918141625p:plain

ですが見てもらってもわかる通り、垂直タブのための機能と言ってもよさそうです。

この機能をオフにするとどうなるか

オフにすると、以前と全く同じ表示になります。 ちなみに以前まではどうだったかというとこんな感じです。

f:id:masatsuna:20210918141408p:plain

どのファイルがどのプロジェクトの物かちょっとわかりずらいですよね。 色で分けてくれた方が、より視覚的にわかりやすくなって良い感じです。

設定方法

[ツール] メニュー > [オプション] を選択します。

f:id:masatsuna:20210918120021p:plain

左側メニューで [環境] > [タブとウィンドウ] を選択し、右側ペインで [プロジェクト別にドキュメント タブを色分けする] にチェックを入れます。

f:id:masatsuna:20210918122123p:plain

これでタブの色分け機能が利用できます。

なお垂直タブにするためには、 [タブのレイアウト設定] のドロップダウンを変更してください。 こちらの過去記事も参考になるかと思います。

tsuna-can.hateblo.jp

おことわり

この機能はプレビュー版の Visual Studio 2022 に搭載されている機能であり、現在広くサーベイを行っている最中です。 製品版では仕様が変更されている可能性もありますのでご了承ください。

Logic Apps から Azure の REST API をマネージド ID を使って呼び出す

f:id:masatsuna:20210916005110p:plain

マネージド ID使っていますか? 徐々に使えるリソースが増えてきて、最近では多くのケースで使うことが増えているように思います。 非常に便利で強力な機能ですよね。

今回は Logic Apps から Azure の REST API をマネージド ID を使って呼び出す手順を解説します。 例によって初心者向けに、できる限り手順を省かず説明していきます。

なおマネージド ID については、過去に解説した記事がありますのでそちらを参照してください。

tsuna-can.hateblo.jp

リソースグループ、 Logic Apps の作成

まずは今回使用するリソースグループ [rg-resource-manager] を作成します。 作成したリソースグループに、 [logic-get-resources-by-rg] という名前の Logic Apps を追加しましょう。 今回は従量課金のプランで作成しています。

Logic Apps のリソースを Azure Portal から作成すると、いきなりデザイナー画面が立ち上がります。 まずは下回りの設定を行っていくので、一旦この画面は閉じてしまいましょう。

f:id:masatsuna:20210915233044p:plain
一旦 Logic Apps デザイナーを閉じる

システム割り当てマネージド ID の有効化

次に今回の主役であるマネージド ID を有効にしていきます。 マネージド ID にはシステム割り当てマネージド ID とユーザー割り当てマネージド ID があります。 今回は最も簡単に使用できるシステム割り当てマネージド ID を使っていきます。

Logic Apps のメニューから、 [設定] > [ID] を選択します。

f:id:masatsuna:20210915233324p:plain
[ID] を選択

[システム割り当て済み] のタブで、 [状態] のトグルを [オン] に設定して [保存] ボタンを押下します。

f:id:masatsuna:20210915233432p:plain
システム割り当てマネージド ID の有効化

これでシステム割り当てマネージド ID が作成されました。 正常に処理されていれば、 [オブジェクト (プリンシパル) ID] の GUID 値が表示されているはずです。 この値は後で設定確認のために参照するのでメモしておきましょう。

f:id:masatsuna:20210915234335p:plain
システム割り当てマネージド ID の有効化後

呼び出す対象リソースへのロール割り当て

今回は以下の REST API を呼び出すようにしたいと思います。

docs.microsoft.com

情報取得対象のリソースグループは、自分のサブスクリプション内にあるものならどれでも、クエリ文字列で指定できるようにします。 そのため、先ほど作成したマネージド ID が、サブスクリプションの読み取り権限を持つように設定していきます。 これによって、そのサブスクリプション配下にある全リソースの読み取り権限を割り当てることができます。

早速設定を行っていきましょう。 リソースの一覧から [サブスクリプション] を選択し、ご自身のサブスクリプションを選択してください。 ここで参照できる [サブスクリプション ID] は、後程使用するので控えておきましょう。 左側のメニューから [アクセス制御 (IAM)] を選択し、 [アクセスの確認] タブで [ロールの割り当ての追加] ボタンを押下します。

f:id:masatsuna:20210915234405p:plain
ロール割り当ての追加を選択

続いてマネージド ID に対して割り当てるロールを選択します。 今回は情報を取得するだけですので、 [閲覧者] のロールを選択します。 実行したい REST API の種類に応じて、割り当てるロールを選択するようにしてください。

ロールには、 Azure がある程度便利に使えるようまとめてくれた組み込みのロールと、自分で作りこむカスタムロールがあります。 ほとんどのケースで組み込みロールを使えば問題ありません。 細かな制御が必要になってからカスタムロールの作成を検討しましょう。

f:id:masatsuna:20210915234756p:plain
[閲覧者] ロールを選択

続いて選択したロールにマネージド ID を割り当てます。 [アクセスの割り当て先] から [マネージド ID] を選択して [メンバーを選択する] のリンクを押下します。

f:id:masatsuna:20210915235031p:plain

[マネージド ID の選択] 画面で [システム割り当て] のラジオボタンを選択し、先ほど作成した Logic Apps のシステム割り当てマネージド ID を探します。 作成した Logic Apps のリソース名 [logic-get-resources-by-rg] を見つけたら、それを選択しましょう。

f:id:masatsuna:20210915235330p:plain
Logic Apps のマネージド ID を選択

[選択したメンバー] に上述のマネージド ID が移動したら、 [選択] ボタンを押下します。

f:id:masatsuna:20210915235430p:plain
[選択] ボタンを押下

すると元の画面に選択したマネージド ID の情報が追加されます。 念のため [オブジェクト ID] の値を参照し、メモしておいたオブジェクト ID と等しいことを確認します。 問題なければ [レビューと割り当て] を押下します。

f:id:masatsuna:20210915235645p:plain
[レビューと割り当て] を押下

再度確認画面に移りますが、 [レビューと割り当て] をもう一度押下します。

f:id:masatsuna:20210915235739p:plain
[レビューと割り当て] ボタンを再度押下

これで Logic Apps のマネージド ID が、サブスクリプションの閲覧者として登録できました。

今回は非常に広い範囲に対するアクセス許可を与えますが、呼び出す REST API が特定のリソースにだけアクセスするのであれば、ロール割り当てするリソースを絞り込むようにしてください。 最小権限の原則をできる限り守りましょう。

Logic Apps の実装

ここからは Logic Apps に戻り、処理の本体を実装していきます。 今回は HTTP 要求を受けたら、クエリ文字列に設定されているリソースグループの情報を取得して、そのまま返すような Logic Apps を作ろうと思います。

HTTP 要求の受信時トリガーの設定

[logic-get-resources-by-rg] のリソースに移動して、[ロジック アプリ デザイナー] を開きます。 まずは HTTP 要求を受け取れるように、 [HTTP 要求の受信時] のトリガーを選択します。 今回は情報を取得するための処理なので、メソッドは [GET] に設定し、その他の項目はデフォルトのままにします。

f:id:masatsuna:20210916000317p:plain
HTTP 要求の受信時の設定

Azure REST API 呼び出しの設定

次に Azure REST API を呼び出す箇所を作っていきます。 REST API の呼び出しは HTTP リクエストを送ることに他ならないので、 [HTTP] のアクションを選択して追加します。

f:id:masatsuna:20210916000537p:plain
[HTTP] のアクションを追加

次に Azure REST API の設定を行います。 Azure REST API のリファレンスにある通り、 HTTP リクエストを組み立てます。 今回は以下の API を呼び出します。

Resources - List By Resource Group - REST API (Azure Resource Management) | Microsoft Docs

ですので、基本的な設定は下記の通りとなります。

f:id:masatsuna:20210916000909p:plain
REST API 呼び出しの設定

ここでポイントとなるのは、 [URI] の項目でトリガーをキックした URL のクエリ文字列から、検索対象のリソースグループ名を取得している個所です。 リソースグループ名を設定したい箇所にカーソルを合わせて [動的なコンテンツの追加] リンクを押下すると、図のようなウィンドウを表示できます。 [式] のタブを選択して、以下のコードを打ち込んでください。

triggerOutputs()['queries']['resourceGroup']

これで HTTP トリガーのクエリ文字列に設定されている resourceGroup という名前のキーの値を URI に組み込むことができます。 REST API を実行するので、 HTTP ヘッダーに application/json の Content-Type を設定しておきましょう。

ここまでの実装が終わったら、最下部の [Add new parameter] のドロップダウンを開きます。 そして [認証] にチェックを入れてドロップダウンを閉じましょう。

f:id:masatsuna:20210916001755p:plain
[認証] にチェックを入れてドロップダウンを閉じる

[認証の種類] を選択する画面になるので、 [マネージド ID] を選択します。

f:id:masatsuna:20210916001913p:plain
[マネージド ID] を選択

またマネージド ID の種類を [システム割り当てマネージド ID] に設定します。 今回はシステム割り当てマネージド ID を使用するので、 [対象ユーザー] の部分は未入力で OK です。

f:id:masatsuna:20210916002037p:plain
[システム割り当てマネージド ID] を選択

HTTP 応答の設定

次に HTTP の応答を返す処理を組み込みます。 今回はことを簡単にするため、前段で呼び出した REST API の結果をそのまま返却するようにします。 一応 [状態コード] と [本文] を返却するようにしておきます。

f:id:masatsuna:20210916002254p:plain
HTTP 応答の設定

これで処理の実装が終わりました。 最終的な処理の流れは以下のようになります。

f:id:masatsuna:20210916002333p:plain
最終的な処理の流れ

ここまで出来たら上部の [保存] ボタンを押下しておきましょう。

動作確認

これで実装が完了したので、動作確認を行ってみましょう。 画面上部の [トリガーの実行] ドロップダウンを開いて [ペイロードで実行] を選択します。

f:id:masatsuna:20210916002542p:plain
[ペイロードで実行] を選択

ペイロードで実行を選択すると、テスト実行する HTTP リクエストを Portal 上で組み立てて実行することができます。 以下のようなケースではこちらを選択しましょう。

  • GET 以外のメソッドを使用するとき
  • JSON をリクエスト本文に設定するとき(POST/PUT などのとき)
  • GET でもクエリ文字列や URL のパスを指定したいとき
  • HTTP ヘッダーに値をつめたいとき

今回はクエリ文字列にリソースグループの名称を設定したいので、以下のように設定します。 なお指定しているリソースグループ名は、今回作成している Logic Apps の配置されているリソースグループです。 このリソースグループには、今回作成している Logic Apps 以外何も置かれていません。

f:id:masatsuna:20210916002928p:plain

設定が終わって [実行] ボタンを押下すると、作成した Logic Apps が起動し、応答メッセージが表示されます。

f:id:masatsuna:20210916003115p:plain
応答メッセージ

配置してある Logic Apps のリソース情報を取得できていることが確認できました。

まとめ

今回は Logic Apps とシステム割り当てマネージド ID を使って、 Azure の REST API を呼び出す方法を解説しました。 マネージド ID を使用することで、認証周りの処理の作りこみを大きく削減できます。 また今回は特に触れていませんが、ロールの作りこみや対象リソースの絞り込みによって、細かな権限制御を行うこともできます。 ぜひ積極的に使っていきたいですね。

VSCode の Remote - Containers で Jekyll の開発環境を作る

f:id:masatsuna:20210912234349p:plain

GitHub Pages では、マークダウンから HTML への変換に Jekyll という Ruby ベースのツールを利用します。

jekyllrb-ja.github.io

単純に GitHub リポジトリと連携する場合、ローカル開発環境は特に気にしなくてもよいのですが、 Windows のローカル環境で Jekyll の動作確認しようとすると割とめんどくさいことになります。 Windows 環境だと公式サポートもなくて、実際に動かしてみても動作不安定な感じでした。

そんな時はコンテナー使えばいいじゃない、ということで、 Visual Studio Code の Remote - Containers extension を使って、 Jekyll のローカルビルド環境を作ってみました。

やりたいこと

私の場合はこんなことを考えていました。

  • Jekyll のローカルビルド、ローカル確認環境を Windows 上に作りたい
  • できる限りローカルの環境を汚したくない
  • ローカル環境のブラウザーで作成したサイトの確認を行いたい
  • マークダウンの保存をしたら即時サイトの確認をしたい
  • VS Code の Remote - Containers 上で Git リポジトリと連携したい

動作確認環境

  • Windows 10 21H1
  • Visual Studio Code 1.60.0
  • Remote - Containers extension v0.194.0
  • Docket Desktop 4.0.0
  • Jekyll 4.2.0(docker composeではタグ未指定、今回の検証に使用したバージョン)

このサンプルでできること

VS Code を立ち上げて数クリックで Jekyll の開発ができる

起動するときは GUI 上でポチポチするだけです。

f:id:masatsuna:20210912224845p:plain
左下のボタンを押して [Reopen in Container] を選択

Visual Studio Code がコンテナーに接続する

Docker コンテナーの起動と接続は Remote - Containers の拡張機能が docker compose 経由でやってくれます。 起動するとターミナルがコンテナーに接続してくれます。

f:id:masatsuna:20210912225437p:plain
起動するとターミナルが Dev Container と接続

ブラウザーが立ち上がる

先ほどの GUI ポチポチをやると、ローカルマシンのブラウザーが開きます。 そして起動した Jekyll の開発サーバーに接続してくれます。

f:id:masatsuna:20210912225734p:plain
ブラウザーが起動する

初回起動時やキャッシュが効いていないときなど、開発サーバーの起動まではそれなりに時間を要します。 それなりに高スペックなマシンでも、最初は 1 分くらいかかると思います。 ブランクページに飛んでしまったときは、サーバーが立ち上がるのを待ってから、画面をリロードしてみてください。

マークダウンを保存すると即画面に反映

Jekyll はブラウザーをオートリロードするための仕組みがあります。 今回のサンプルではそのあたりも組み込んでみました。 以下のように、マークダウンファイルを保存するとブラウザー画面もオートリロードされます。

f:id:masatsuna:20210912231945g:plain
マークダウンを保存するとブラウザーがオートリロード

コンテナーに接続しながら VSCode 上から Git リポジトリも操作可能

2 枚 Visual Studio Code を立ち上げたり、コマンドラインで操作しなくても、普通に開発を進めることができます。

サンプルコード

このポストで解説しているサンプルコードは以下で公開しています。 環境構築手順等は README を参照してください。

github.com

Windows 11 は 10月5日リリースらしい

f:id:masatsuna:20210831230254p:plain

Windows 11 リリース日決定

本日 Windows 11 の GA についてマイクロソフトからアナウンスがありました。 どうやら 2021年10月5日 リリース らしいです。 米国時間だと思うので、日本時間だと10月6日になるかもしれません。

マイクロソフトからの発表は以下にありますので、気になる方はチェックしてみてください。

blogs.windows.com

Windows Server 2022 がリリースされたので、近いうちに GA されそうだなーと思っていましたが、年末商戦に間に合わせる格好で出してきましたね。

システム要件

マイクロソフトのサイトで直接見てください。 日本語版も出ているのでわかりやすいです。

www.microsoft.com

ざわつきのあった TPM 2.0 についても、システム要件としてちゃんと記載があります。 インストールメディアからインストールするときはチェックがスキップできるとか、裏街道はいろいろあるみたいですが、設定できるマシンは設定したほうが良いと思います。

TPM 2.0 の設定方法は、 ASUSMSIBIOS について過去に書いてますので参考までにどうぞ。

tsuna-can.hateblo.jp

tsuna-can.hateblo.jp

Hyper-V 上で動かしたい場合はこちらが参考になると思います。

tsuna-can.hateblo.jp

ここ 2 年くらいの間にリリースされた PC なら、ほとんど問題なくクリアできる要件になっています。 ただし、発売当初割安だった製品は、この期間にリリースされたものでも旧世代の CPU を使っているケースがあるので、よく確認してみてください。 大手国内メーカーの PC でも、 2019 年発売モデルだと未対応のものが混ざっていそうです。 Intel Core だと第 8 世代以降、 AMD Ryzen だと Zen2 世代以降が対応 CPU として掲載されています。

docs.microsoft.com

docs.microsoft.com

条件が緩和されるかもしれない、という話がちらちら聞こえたりしますが、今のところシステム要件に大きな変更は入っていないように見えます。

インストール手順

Hyper-V 上の VM に ISO ファイルからインストールしてみました。 一部 VM の設定も混じっていますが、インストール手順は変わらないはずです。

tsuna-can.hateblo.jp

Windows 10 はいつまで使えるの?

2025年10月14日まで延長サポートは続きます。 ただし、古いマイナーバージョンのままだとサポートしてもらえなくなるので、 Windows Update はなるべくやるようにしましょう。 なおメインストリームサポートは 2020 年に終了しています。 今後はセキュリティアップデートが中心となるはずです。 いずれにせよ終了まであと 4 年くらいなので、更新計画はお早めに。 半導体不足はもうしばらく続きそうなのが怖いところです。

Insider 版の感想

6 月から Windows 11 の Insider Preview を触ってきていて、個人的には結構いい感じに仕上がっていると思っています。 最初のころの Preview ではエクスプローラーがクラッシュしたり、タスクバーがフリーズしたり、いろいろ不具合もあったのですが、ここのところそう言った問題は起きなくなってきています。 デザイン面も、設定アプリの UI 変更は結構いい感じなんじゃないかと思います。 リボン UI も変わって、必要なものだけが配置されるようになりました。

ただ、改悪なんじゃないか?と思われることもいくつかあります。 まず、タスクバーの設定自由度が大きく制限されています。 この辺は特に賛否両論起きそうです。 タスクバーを下側にしか配置できないので、カスタムしていた人はちょっといやかなーと思います*1。 スタートボタンが左下にない(全体的にタスクバーの中心方向にアイコンが寄る)のも、昔からの Windows ユーザーとしては面喰います。 私はなんとなく左下にマウスカーソルをもっていってしまう癖があるので。。。 設定変更すれば左側にボタンを寄せることはできますが、なんかダサくなるので変えたくないんですよね。

あとリボン UI の廃止に伴って、利用頻度の低い操作ボタンにキャプションがつかなくなりました。 ボタンの絵を見ても直感的に何をしてくれるボタンなのかわからない(≒覚えてない)ので、ますます押す機会が減りそうです。 エクスプローラーのボタンは以下のような感じなのですが、みんなわかるのかなぁ。。。 キーボードショートカットで代替できるのでほとんど押すことはないと思いますが、それはどうなのよ、と思います。

f:id:masatsuna:20210831231648p:plain

なおコントロールパネルは Windows 11 でも変わらず存在するようです。 設定アプリは結構充実してきているのに、いつまでもなくせない状態になっている感じもします。 個人的にはなくしてほしくないですが、管理コンソールと同様、 UI の世界観がまるで違うのでちょいダサですね。

エンプラ系だとついに IE がプリインストールされなくなります。 宴じゃー。 かわって Chromium版 Edge がプリインストールされ、古いシステムは IE モードを使ってね、ということになります。

まとめ

Windows 11 のリリースがもう間もなくというところまで来ましたので、 Preview 版の感想を全体的にまとめてみました。 細かいところだとまだまだ言いたいことはあるのですが、このくらいにしておいてやろう。

Windows 11 に対応している PC

現行モデルはほとんど対応していると思って問題ありません。 古いモデルを買う場合は注意しましょう。

マイクロソフトのサイトで紹介されていた PC をここにも転載しておきます(2021/08/31 現在)。 購入当初は Windows 10 となりますが、 Windows 11 へのアップデートが可能とされています。 なお実際のアップデート可否については責任取れませんので、各自で十分に調査するようにお願いします。








*1:レジストリをいじると変えることができる、という話をどこかで見たことがあるような気がしますが、設定アプリからは変更できないみたいです。

Hyper-V 使うなら Ryzen はやっぱりだめだった話

f:id:masatsuna:20210826023816p:plain

年末に Ryzen 9 5900X で自作 PC を組んで、いろいろ遊び倒してきました。 ゲームは全然やってないんですが、主にアプリケーション開発に活用しています。 やっぱり早いって素敵、という感想です。 お気に入りかどうかという意味ではすごく気に入っています。


Ryzen の残念な点

前々からわかっていたことなのですが、 Windows OS と Ryzen CPUは、それほど相性の良い組み合わせではありません。 特に 仮想化まわりの機能IntelAMD では差があります。 プログラム開発用途で使っていて、今回この問題を再認識したのでご紹介しておきます。

RyzenHyper-V入れ子にできない

私はプログラム開発を Hyper-V 上の仮想マシンで行っています。 プログラム開発を行うと、知らぬ間にレジストリに値が追加されていたり、環境変数が無意味に増えていたり、環境が汚れやすくなります。 最初のうちはいいのですけど、だんだんそういうゴミがたまっていって、ある日競合したりして、めんどくさいことになるわけです。 それに新しい OS が出たり、新しい言語が出たりしたとき、ちょっと試すのに仮想マシンは最適です。 そんなこともあり、開発環境は Hyper-V 上の仮想マシン、というのが私のやり方として定着しました。 仮想マシンにしておけば、いらなくなった瞬間に破棄できますし、新たなマシンを作るのも簡単です。

昨今のプログラム開発と言えば、 Docker の出番が非常に増えています。 Windows 上で Docker を使う場合は、 Docker Desktop というツールをインストールし、 WSL2 を使えるようにする、というのが現状の基本的なやり口です。

これらのツールを動かすためには、 BIOS レベルで設定を行わなければなりません。 Ryzen の場合は、以下の記事でも紹介したとおり、 SVM Mode を有効にしなければなりません。

tsuna-can.hateblo.jp

ですが、この設定は、あくまで物理マシンの BIOS の話をしています。

私がやりたいのは、 Hyper-V 上に作成した仮想マシン内で Docker Desktop を動かすことです。 それを行うためには、 Hyper-V のホスト側で「仮想 CPU の仮想化」ができるようにしなければなりません。 しかし、これができるのは Intel CPU だけなんですよね。 詳細は以下の記事に解説されています。

docs.microsoft.com

Ryzen CPU では私のやりたいことはできないぞ、というオチでした。

まとめ

今回は「できない」話をしてしまいました。 Ryzen のシェアもあがってきたので、 Windows にももう少し頑張ってもらえるとユーザーとしてはありがたいのですが。。。

2021/10/19 更新

AMD プロセッサでも Windows 11 または Windows Server 2022 から、 Hyper-V入れ子ができるようになります。 これで Ryzen のデメリットがまたひとつなくなりますね。

tsuna-can.hateblo.jp

Seagate FireCuda 530 あらわる

f:id:masatsuna:20210813004406p:plain

2020 年末に構築した Ryzen 9 5900X 搭載の PC 、こちらには Seagate FireCuda 510 1TB の m.2 SSD を搭載しています。 詳細はこちらに書いている通りです。

tsuna-can.hateblo.jp

この記事でも書いた通り、 FireCuda 510 は仮想マシンのイメージ置き場として活躍してもらっています。 現状 3 台の仮想マシンを置いていて、同時起動しても問題ないレベルで使えています。 PCIe 3.0 接続ですが、性能的には十分満足できるレベルです*1

Seagate FireCuda 530

そんな中、なんと Seagate から FireCuda 530 がリリースされるというニュースが飛び込んできました。 520 から PCIe 4.0 接続ができるようになったものの、 I/O 性能は微妙で、価格帯も少々お高め、という割と謎な位置づけの製品でした。 530 はどんなもんかね、とスペック表を見てみると、 520 から大幅に性能向上している様子が見て取れました。

1TB モデルで比較するとこんな感じです。

項目 FireCuda 520 FireCuda530
シーケンシャル読取り 5000MB/s 7300MB/s
シーケンシャル書込み 4400MB/s 6000MB/s
ランダム読取り 760,000 IOPS 800,000 IOPS
ランダム書込み 700,000 IOPS 1,000,000 IOPS

データシート上は、 Samsung の 980 Pro とほとんど変わらない値になっています。 お値段も 26,000 円前後になるようで、十分戦える価格設定です。


あとこのモデル、なんと 4TB モデルが出るそうです。 4TB モデルの発売日は 2021 年 9 月 10 日で、まだもう少し先だそうです。 PCIe 4.0 対応、かつ高性能な 4TB モデルは今まで存在していませんでした。 両立を求める人には最適な選択になりそうです*2

メインの用途は PS5 の拡張用?

PS5 の拡張用 SSD の要件って結構厳しい仕様になっていますよね。 FireCuda 530 はこの要件にしっかり合致するものになっています。 しかも 4TB クラスの m.2 SSD で要件にあうのは FireCuda 530 くらいしかないんじゃないかと思います。 PS5 の拡張用 SSD としてのほうが、もしかしたら人気が出るかもしれませんね。

まとめ

あまり注目度の高くない SeagateSSD ですが、カタログスペック通りの性能が出るなら良い選択肢になりそうな気がします。 特に大容量モデルの登場は、うれしい人にはうれしいはずです*3。 誰かレビューしてくれないかな。

*1:仮想マシンのイメージ統合の速度が爆速になったのが本当にうれしい。安心してスナップショットとれる。

*2:その分値段が高いけど。

*3:主にPS5 ユーザーとか。

Visual Studioで巨大なソリューションを扱うときに便利な機能

f:id:masatsuna:20210810221019p:plain

ここのところかかわっているプロジェクトでは、 1 ソリューションのプロジェクト数が 500 を超えていて、 Visual Studio でソリューションを開くことがなかなか困難なレベルになっています。 そういう巨大なソリューションを取り扱う場合、 Visual Studio 2019 以降で使える便利な機能がありますので紹介します。

環境

プロジェクトをアンロードした状態でソリューションを開く

巨大に育ってしまったソリューションの場合、そもそも貧弱なマシンでは Visual Studio で開くことができません。 ソリューションファイルをダブルクリックして、ソリューションを直接 Visual Studio で開く、というオペレーションはあきらめましょう。 そういったときは、先に Visual Studio を起動して、起動メニューからソリューションを開くメニューを選択するか、メニューバーから以下のようにソリューションを開くメニューを選択します。

f:id:masatsuna:20210807201242p:plain
メニューバーからソリューションを開く

開くソリューションを選択する際、 [プロジェクトを読み込まない] にチェックを入れてから開きましょう。 これで全プロジェクトをアンロードした状態で開くことができるため、大きなソリューションでも問題なく開けます。

f:id:masatsuna:20210808194418p:plain
プロジェクトをアンロードした状態でソリューションを開く

プロジェクトを読み込む

このようにしてソリューションを開くと、全プロジェクトがアンロードした状態になります。

f:id:masatsuna:20210808195833p:plain
すべてアンロードした状態で開く

実行に必要なプロジェクトを右クリックして、 [プロジェクトの再読み込み] を選択することでロードすることができます。

f:id:masatsuna:20210808200007p:plain
プロジェクトの再読み込み

これで大量のプロジェクトがあるソリューションでも、必要なプロジェクトだけを選択してロードすることができます。

依存関係にあるプロジェクトをまとめて読み込む

ソリューション構造が複雑だったり、 1 つのプロジェクトが別のプロジェクトをいくつも参照するような構造の場合、ひとつひとつプロジェクトを読み込むのは非常に手間です。 そういった場合は、プロジェクトを右クリックして [依存関係のあるプロジェクトを再読み込み] を選択しましょう。

f:id:masatsuna:20210808200203p:plain
依存関係のあるプロジェクトを読み込む

これで、依存関係のあるプロジェクトをまとめてロードできます。 スタートアッププロジェクトに対してこれを行うことで、最低限のプロジェクトだけをロードできます。

f:id:masatsuna:20210808200606p:plain
必要なプロジェクトのみを再読み込み

ソリューションフィルター

必要なプロジェクトだけ読み込んだ状態にすると、 Visual Studio を再起動しても、同じ状態を再現できます。 しかし、この読み込み設定を、ほかの人と共有することができません。 他の人と設定を共有したい場合は、ソリューションフィルターと呼ばれる機能を使いましょう。

一部のプロジェクトをアンロードした状態で、ソリューションを右クリックして [ソリューションフィルターとして保存] を選択します。

f:id:masatsuna:20210808204315p:plain
ソリューションフィルターとして保存

すると、「*.slnf」という拡張子のファイルを保存できます。 これで、アンロードした状態をファイルに保存できます。

f:id:masatsuna:20210808204550p:plain
*.slnfファイルとして保存

*.slnf ファイルは、 JSON 形式のファイルです。 元のソリューションファイルは何だったのかと、読み込んでいるプロジェクトの一覧が以下のように記述されています。

{
  "solution": {
    "path": "eShopOnWeb.sln",
    "projects": [
      "src\\ApplicationCore\\ApplicationCore.csproj",
      "src\\BlazorAdmin\\BlazorAdmin.csproj",
      "src\\BlazorShared\\BlazorShared.csproj",
      "src\\Infrastructure\\Infrastructure.csproj",
      "src\\Web\\Web.csproj"
    ]
  }
}

*.slnf ファイルは、 Visual Studio に直接読み込ませて使用することができます。 上記のようにして作成した *.slnf ファイルを Visual Studio で開くと、以下のようになります。

f:id:masatsuna:20210810211908p:plain
*.slnf ファイルを開く

アンロードしたプロジェクトは、その存在すら見えないようになっているのが特徴です。 このまま実行すると、ロードされているプロジェクトだけがビルドされます。

アンロードしたプロジェクトを再表示する

ソリューションフィルターを利用すると、一部のプロジェクトのみフィルタリングして表示することができます。 フィルタリングの結果、非表示になってしまったプロジェクトを再表示することもできます。

アンロードされたすべてのプロジェクトを再表示する場合は、ソリューションを右クリックして [アンロードされたプロジェクトを表示] で表示できます。

f:id:masatsuna:20210810212340p:plain
アンロードされたプロジェクトを表示

ソリューションフォルダーでソリューション内を階層化している場合は、ソリューションフォルダーの単位でも同様の操作ができます。 ソリューションフォルダーを右クリックして [フォルダーの再表示] を選択しましょう。

f:id:masatsuna:20210810215044p:plain
フォルダーの再表示

これでアンロード状態のプロジェクトを再表示できます。

複数の *.sln ファイルを用意することとの違い

こういった問題が発生したとき、数年前までは複数の *.sln ファイルを目的別に用意して対処するケースが多かったのではないでしょうか。 たしかにそれでも対処はできていたのですが、開発用の *.sln ファイルが乱立しやすく、回避策の色合いが濃かったように思います。 複数の *.sln ファイルを用意してしまうと、全部入りソリューションのプロジェクト追加/削除を忘れてしまうことが(私は)よくありました。 ソリューションフィルターなら、元の *.sln をフィルタリングしているだけなので、全部入りソリューションファイルの破壊を防ぐことができます。

ただし、プロジェクトを追加したり削除したりしたときは、ソリューションフィルターファイルの更新を行わなければならないことに注意しましょう。 ソリューションフィルターを使ってソリューションを開いている場合、ソリューションフィルターに影響のあるような更新をソリューションに対して行うと、以下のようなメッセージがソリューションエクスプローラーに表示されます。

f:id:masatsuna:20210810215539p:plain
ソリューションフィルターの更新

ここで [ソリューション フィルターの更新] リンクを押下すると、開いているソリューションフィルターに関しては設定を更新してくれます。 複数のソリューションフィルターを用意している場合、別のソリューションフィルターには変更が反映されないため注意しましょう。

またソリューションフィルターは、表示するプロジェクトをフィルタリングするためにあります。 そのため、存在しないプロジェクトを前述した JSON ファイルの projects に設定しても、その設定は無視されてしまいます。 このあたりも気を付けておかないと、無駄なゴミ設定がたまってしまうかもしれません。 用法容量には気を付けないといけませんね。

まとめ

今回は巨大なソリューションを取り扱うプロジェクトで便利なソリューションフィルターを紹介しました。 巨大なソリューションをチーム開発するようなユースケースで、非常に使い勝手の良い機能だと思います。 ただし、 *.slnf ファイル自体のメンテナンスをおざなりにすると、設定がカオスになっていく可能性も感じます。 *.slnf ファイルを開発者間で共有する場合は、しっかりメンテナンスすることを意識したほうがよさそうです。 逆に言うと、必要がなければ構成管理に紛れ込まないように .gitignore とかでコントロール*1したほうが良いかもしれませんね。

*1:GitHub とか Azure DevOps で Git リポジトリを作ったときに生成できる .gitignore には、2021/08/10 現在、 *.slnf を除外する設定は入っていません。

Entity Framework Core でテーブルスキーマを更新する

f:id:masatsuna:20210711202657p:plain

Entity Framework Core を利用して、テーブルのスキーマを更新する方法について解説していきます。 今回は、前回構築したアプリケーションを利用して、テーブルのスキーマを更新します。

tsuna-can.hateblo.jp

環境

データモデルにカラムを追加する

今回は、 Product モデルクラスに Publisher プロパティを追加します。

public class Product
{
    public long ProductId { get; set; }
    public string ProductName { get; set; }
    public string ProductDescription { get; set; }
    public decimal Price { get; set; }
    public string Publisher { get; set; }  // ★追加行
    public long ProcuctCategoryId { get; set; }
    public byte[] RowVersion { get; set; }
    public ProductCategory ProductCategory { get; set; }
}

DbContext の修正

続いて、 OnModelCreating メソッドの修正も行っていきます。 新たに Publisher プロパティを追加したので、それに対応するカラムの設定を行います。 今回は 256 文字の文字列長制限を指定するだけとしています。

またカラムの追加に伴って、初期データの登録処理も修正を行っています。 前回の記事で作成してあった初期データに、 Publisher のデータも追加しておきます。

public class ProductDbContext : DbContext
{
    // コンストラクター、プロパティは省略

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Product>(entity =>
        {
            // テーブル名の設定
            entity.ToTable("Products");

            // 主キーの設定
            entity.HasKey(product => product.ProductId);

            // 各カラムの制約条件、カラム名の設定
            entity.Property(product => product.Price)
                .HasColumnType("decimal(18,0)");
            entity.Property(product => product.ProductName)
                    .HasColumnName("Name")
                    .HasMaxLength(256)
                    .IsRequired();
            entity.Property(product => product.ProductDescription)
                .HasMaxLength(1024);
            entity.Property(product => product.Publisher)  // ★追加行
                .HasMaxLength(256);

            // 外部キー制約の設定
            entity.HasOne(product => product.ProductCategory)
                    .WithMany(productCategory => productCategory.Products)
                    .HasForeignKey(product => product.ProcuctCategoryId)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("FK_Products_ProductCategories");

            // 行バージョンカラムの設定
            entity.Property(product => product.RowVersion)
                    .IsRequired()
                    .IsRowVersion()
                    .IsConcurrencyToken();

            // マスターデータの登録(Publisher の値を追加設定)
            entity.HasData(new Product { ProductId = 1, ProductName = "C#の本", ProcuctCategoryId = 1, Price = 2000, Publisher = "DOTNET" });
            entity.HasData(new Product { ProductId = 2, ProductName = "Visual Studioの本", ProcuctCategoryId = 1, Price = 2200, Publisher = "DOTNET" });
            entity.HasData(new Product { ProductId = 3, ProductName = ".NETの本", ProcuctCategoryId = 1, Price = 2500, Publisher = "DOTNET" });
            entity.HasData(new Product { ProductId = 4, ProductName = "冷蔵庫", ProcuctCategoryId = 2, Price = 150000, Publisher = "HUTABISHI" });
            entity.HasData(new Product { ProductId = 5, ProductName = "トランプ", ProcuctCategoryId = 3, Price = 280, Publisher = "HONTENDO" });
        });

        // ProductCategories の方は省略
    }
}

Migration の追加作成

ここまで来たら、後は前回同様、 Migration を作成していきます。 作成前に、一度ソリューションをビルドしておきましょう。 ビルドが完了したら、 Visual Studio 内のターミナルから Migration を作成します。 ターミナルについてはこちらを参照してください。

tsuna-can.hateblo.jp

ターミナルで `DbContext' を継承したクラスがあるプロジェクトのルートディレクトリに移動します。 移動したら、以下のコマンドを実行します。

dotnet ef migrations add AddPublisherColumn

今回は Publisher カラムを追加する修正を行っているため、それを端的に表すように Migration の名前を付けています。 処理が正常に完了すると、 Migrations ディレクトリに作成した Migration が追加されます。

f:id:masatsuna:20210710202933p:plain

これで、前回作成した InitialCreate と、 AddPublisherColumn の 2 つの Migration が作成されたことがわかります。

作成した Migration を確認してみる

追加作成した Migration の中身を見てみましょう。

public partial class AddPublisherColumn : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "Publisher",
            table: "Products",
            type: "nvarchar(256)",
            maxLength: 256,
            nullable: true);

        migrationBuilder.UpdateData(
            table: "Products",
            keyColumn: "ProductId",
            keyValue: 1L,
            column: "Publisher",
            value: "DOTNET");

        migrationBuilder.UpdateData(
            table: "Products",
            keyColumn: "ProductId",
            keyValue: 2L,
            column: "Publisher",
            value: "DOTNET");

        migrationBuilder.UpdateData(
            table: "Products",
            keyColumn: "ProductId",
            keyValue: 3L,
            column: "Publisher",
            value: "DOTNET");

        migrationBuilder.UpdateData(
            table: "Products",
            keyColumn: "ProductId",
            keyValue: 4L,
            column: "Publisher",
            value: "HUTABISHI");

        migrationBuilder.UpdateData(
            table: "Products",
            keyColumn: "ProductId",
            keyValue: 5L,
            column: "Publisher",
            value: "HONTENDO");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "Publisher",
            table: "Products");
    }
}

Migration クラスには、 Up メソッドと Down メソッドが自動的に生成されます。 Up メソッドには、修正した内容をデータベースに反映するための処理が記述されています。 今回は Publisher カラムの追加と、初期データの修正を行いました。 その修正内容が、そのまま Up メソッドに反映されていることがわかると思います。

それに対して Down メソッドは、今回行った修正を元に戻すための処理が記述されています。

データベースの更新

Migration を作成したので、続いてデータベースの更新も行います。 データベースの更新は、前回と同様、以下のコマンドで実行します。

dotnet ef database update

このコマンドを実行すると、現在のデータベースに適用されている Migration と、ソリューション内にある Migration を比較して、必要な Migration を順番に適用してくれます。 今回はすでに InitialCreate の Migration が適用されているデータベースが存在する状態でこのコマンドを実行するため、 AddPublisherColumn の Migration のみが適用されます。

実行が正常に完了すると、以下のように Products テーブルに Publisher カラムが追加されます。

f:id:masatsuna:20210711193702p:plain

また初期データの修正分もしっかり反映されています。 Products テーブルのデータを参照すると、 Publisher カラムに初期データがちゃんと登録されています。

f:id:masatsuna:20210711193920p:plain

データベースの状態を元に戻す

誤った Migration を適用してしまった場合など、データベースの状態を特定の Migration に戻すこともできます。 例えば AddPublisherColumn の Migration がすでに適用されている状態で、最初に作成した InitialCreate の Migration の状態に戻すには、以下のコマンドを実行します。

dotnet ef database update InitialCreate

このように、適用したい Migration を database update コマンドの引数に指定することで、指定した Migration に戻すことができます。

Migration の削除

不要な Migration がある場合、最新の Migration に限って削除することができます。 現時点では最初に作成した InitialCreate と、次に作成した AddPublisherColumn という 2 つの Migration が作成されています。 この状態で以下のコマンドを実行すると、最後に作成した AddPublisherColumn の Migration を削除することができます。

dotnet ef migrations remove

このコマンドを実行すると、 Migrations ディレクトリに追加されていたファイルも削除されます。 Migrations ディレクトリの中身は、 dotnet ef migration コマンドを利用して操作するようにしましょう。 プロジェクト内からコマンドを使わずに削除したり追加したりしないようにすることがポイントです。

まとめ

今回は Entity Framework Core でテーブルスキーマを更新する手順や、 Migration の適用、戻し方について解説しました。 Entity Framework Core は非常に便利な機能を持つ反面、使い方を誤るとめんどくさいことになりがちです。 用法容量を守って使いこなしたいですね。

サンプルソースについて

本稿で解説したソースコード一式は、 GitHub で公開しています。

github.com

Entity Framework Core を使ったアプリケーションの開発手順

f:id:masatsuna:20210709005326p:plain

Entity Framework Core を使うと、ほとんど SQL を書かずとも、データベースの作成やデータの更新、取得ができるため、非常に便利だと思います。 今回は Entity Framework Core を業務利用するまでの自分なりの手順を書いていこうと思います。

なお必要な個所はコードを出して解説しますが、全部は解説しきれないため、大幅に省略します。 コード全量については GitHub に置くので適宜参照してください。

環境

dotnet ef が使えない人

本稿では特に解説なく、 dotnet ef コマンドを使います。 dotnet ef コマンドが使えない場合は、以下のコマンドをたたいて、ツールをインストールしましょう。

dotnet tool install --global dotnet-ef

インストール後に再起動しないと使えないこともあります。 詳細は以下のページに解説があります。

docs.microsoft.com

dotnet ef のバージョンが古い人

こちらを参照してください。

tsuna-can.hateblo.jp

なおこの方法では、プレビュー版のモジュールを取得することはできません。 本稿で解説する内容は、 Entity Framework Core 5.0.7 のツールでも問題なく実行できます。 記事の手順をお試ししたい目的であれば、わざわざプレビュー版を入れる必要はありません。

プロジェクト(ソリューション)を作成する

今回はクリーンアーキテクチャを意識して、プロジェクトを 3 つ、以下の通り作成します。

プロジェクト名 概要
ApplicationCore ドメイン層に相当するプロジェクト。Entity とかが含まれる。
Infrastructure DB アクセスの処理を持つプロジェクト。DbContext とかはここに含める。
ConsoleApp アプリケーション本体のプロジェクト。Web アプリケーションのことが多いが、今回はコンソールアプリケーションで。

Visual Studio 上のプロジェクト構成は以下の通りです。 すべて .NET 6 Preview をターゲットとして作成しています。 ApplicationCore と Infrastructure のプロジェクトは [Class library] のプロジェクトテンプレートを、 ConsoleApp のプロジェクトは [Console Application] のプロジェクトテンプレートを使用して生成しています。

f:id:masatsuna:20210630055232p:plain

続いてプロジェクト間の参照を張っていきます。 今回は ApplicationCore が最下層で、それを Infrastructure と ConsoleApp プロジェクトが参照します。 また Infrastructure を ConsoleApp が参照します。 なお今回は、テストプロジェクトの作成を省略します。

モデルの作成

まずはシステムの取り扱うモデルを作成しましょう。 今回はシンプルに、商品と商品カテゴリーをモデル化します。 商品は必ず 1 つの商品カテゴリーを持つ、ということにします。

これを ApplicationCore のプロジェクトに以下のように実装していきます。

f:id:masatsuna:20210630060731p:plain

コードとしてはこんな感じです。

public class Product
{
    public long ProductId { get; set; }
    public string ProductName { get; set; }
    public string ProductDescription { get; set; }
    public decimal Price { get; set; }
    public long ProcuctCategoryId { get; set; }
    public byte[] RowVersion { get; set; }
    public ProductCategory ProductCategory { get; set; }
}

public class ProductCategory
{
    public long ProductCategoryId { get; set; }
    public string Name { get; set; }
    public byte[] RowVersion { get; set; }
    public IList<Product> Products { get; set; }
}

お互いのクラスを相互に参照しあうようなモデルクラスになっています。

NuGet パッケージの追加

続いて Entity Framework Core を利用できるように、 NuGet パッケージを追加していきます。 今回は Infrastructure と ConsoleApp のプロジェクトに以下の通り追加します。

NuGet パッケージ Infrastructureプロジェクト ConsoleAppプロジェクト
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Design -

DbContext の作成

DbContext を継承したクラスの定義とコンストラクターを作成する

作成したモデルクラスを使った DbContext を作っていきます。 Infrastructure プロジェクトにクラスを追加して、 DbContext クラスをを継承するようにします。 今回は ProductDbContext という名前を付けてみました。

DbContext を継承したクラスは、以下のようにコンストラクターを 2 つ用意しておきます。

public class ProductDbContext : DbContext
{
    public ProductDbContext()
    {
    }

    public ProductDbContext(DbContextOptions<ProductDbContext> options) : base(options)
    {
    }
}

これは後で DI を利用することを考慮しての実装です。 汎用性を考えると、このような実装としておくことが個人的にはおすすめです。 なお本稿では 2 つ目のコンストラクターは利用しません。

テーブルに対応するプロパティを定義する

続いてモデルクラスをテーブルに対応させるため、 DbContextDbSet<T> 型のプロパティを以下のように定義します。

public class ProductDbContext : DbContext
{
    // コンストラクターは省略

    public DbSet<Product> Products { get; set; }
    public DbSet<ProductCategory> ProductCategories { get; set; }
}

テーブルの定義

これだけだと、これらのクラスをどのようにテーブルに格納するかが未定義状態になってしまうので、そのあたりの設定を行っていきます。 テーブルの定義は、 'DbContext' クラスの 'OnModelCreating' メソッドをオーバーライドして実施します。

public class ProductDbContext : DbContext
{
    // コンストラクター、プロパティの実装は省略

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Product>(entity =>
        {
            // テーブル名の設定
            entity.ToTable("Products");

            // 主キーの設定
            entity.HasKey(product => product.ProductId);

            // 各カラムの制約条件、カラム名の設定
            entity.Property(product => product.Price)
                .HasColumnType("decimal(18,0)");
            entity.Property(product => product.ProductName)
                .HasMaxLength(256)
                .IsRequired();
            entity.Property(product => product.ProductDescription)
                .HasMaxLength(1024);

            // 外部キー制約の設定
            entity.HasOne(product => product.ProductCategory)
                .WithMany(productCategory => productCategory.Products)
                .HasForeignKey(product => product.ProcuctCategoryId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_Products_ProductCategories");

            // 行バージョンカラムの設定
            entity.Property(product => product.RowVersion)
                .IsRequired()
                .IsRowVersion()
                .IsConcurrencyToken();

            // マスターデータの登録
            entity.HasData(new Product { ProductId = 1, ProductName = "C#の本", ProcuctCategoryId = 1, Price = 2000 });
            entity.HasData(new Product { ProductId = 2, ProductName = "Visual Studioの本", ProcuctCategoryId = 1, Price = 2200 });
            entity.HasData(new Product { ProductId = 3, ProductName = ".NETの本", ProcuctCategoryId = 1, Price = 2500 });
            entity.HasData(new Product { ProductId = 4, ProductName = "冷蔵庫", ProcuctCategoryId = 2, Price = 150000 });
            entity.HasData(new Product { ProductId = 5, ProductName = "トランプ", ProcuctCategoryId = 3, Price = 280 });
        });

        // ProductCategories の方は省略
    }
}

テスト用データベースへの接続設定

続いてローカル環境での開発と、本番環境での利用、両方を想定してデータベースへの接続設定を行います。 この設定は、 DbContext クラスの OnConfiguring メソッドをオーバーライドして実施します。 この例では、ローカル開発用に SQL Server LocalDB を使用しています。

public class ProductDbContext : DbContext
{
    // コンストラクター、プロパティ、メソッドの実装は省略

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (optionsBuilder is null)
        {
            throw new ArgumentNullException(nameof(optionsBuilder));
        }

        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Products;Integrated Security=True");
        }
    }
}

Migration の作成

次に、データベースにテーブルを作成するための元データとなる、 Migration *1 を作成します。 Migration は、原則として DbContext を継承したクラスがあるプロジェクトに作成します。 今回は Infrastructure プロジェクトに DbContext を継承した ProductDbContext クラスを追加ししていますので、 Migration の追加も Infrastructure プロジェクトに対して行います。 Migration を作成する前に、 Infrastructure プロジェクトをビルドしておきましょう。

ビルドが完了したら、 Visual Studio 内のターミナルから Migration を作成します。 ターミナルについてはこちらを参照してください。

tsuna-can.hateblo.jp

ターミナルで `DbContext' を継承したクラスがあるプロジェクトのルートディレクトリに移動します。 移動したら、以下のコマンドを実行します。

dotnet ef migrations add InitialCreate

InitialCreate の部分は任意の名前を付けることができます。 ただし、何を更新したのかわかりやすい名前を付けておくことをおすすめします。

このコマンドを実行すると、 DbContext を継承したクラスがあるプロジェクトに Migrations ディレクトリが作成されます。 その中には、先ほど命名した Migration が生成されます。

f:id:masatsuna:20210708202737p:plain

このクラスには、ここまで作成してきたモデルクラスや、 DbContext を継承したクラスの情報から、データベースを初期化したり、初期データを登録したりする処理が記述されています。

データベースの生成

先ほど作成した Migration を利用して、 SQL Server にデータベースを生成します。 Migration を生成したプロジェクトのプロジェクトファイルがあるディレクトリにターミナルで移動し、以下のコマンドを実行します。

dotnet ef database update

このコマンドを実行すると、 SQL Server 上の Migration の履歴データを参照して、生成した Migration を最新の状態まで適用してくれます。 今回は Migration を 1 つしか作成していません。 また、 SQL Server 上にも対応するデータベースが存在しません。 そのため、このコマンドを実行すると、データベースの生成と、作成しておいた Migration の適用が一気に行われます。

正常に処理が行われると、以下のように SQL Server 上に新しいデータベースが作成されます。 またその中には、今回作成した Products テーブルと、 ProductCategories テーブルも生成されています。

f:id:masatsuna:20210708233929p:plain

もうひとつ、 __EFMigrationsHistory というテーブルも生成されます。 これが適用した Migration の履歴データを保持するテーブルです。 Entity Framework Core の Migration を利用して SQL Server のデータベース生成を行う場合、このテーブルのデータをいじってはいけません。

データアクセスの確認

最後に、正常にデータアクセスができるか確認していきましょう。 今回はデータアクセス周りの処理を Repository として切り出して、その中でデータアクセス処理を行うように組み立てています。 Repository のインターフェースは以下のようにしてみました。

public interface IProductRepository
{
    IList<ProductCategory> GetAllCategories();
    IList<Product> GetProducts(ProductCategory productCategory);
}

具象クラスは以下の通りです。

public class ProductRepository : IProductRepository
{
    private readonly ProductDbContext context;

    public ProductRepository(ProductDbContext context)
    {
        this.context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public IList<ProductCategory> GetAllCategories()
    {
        return this.context.ProductCategories.ToList();
    }

    public IList<Product> GetProducts(ProductCategory productCategory)
    {
        if (productCategory is null)
        {
            throw new ArgumentNullException(nameof(productCategory));
        }

        return this.context.Products
            .Where(p => p.ProcuctCategoryId == productCategory.ProductCategoryId)
            .ToList();
    }
}

本格的にこれらを使っていく場合は、 DI コンテナーを使用していくところです。 しかし、今回はあくまで Entity Framework Core の使い始めの部分に焦点を当てているため、そういった部分は省略してしまいます。 取り急ぎ Main メソッドから ProductRepository を呼び出して、ちゃんと動いているか確認してみます。

static void Main(string[] args)
{
    using ProductDbContext context = new ProductDbContext();
    IProductRepository repository = new ProductRepository(context);

    var categoryies = repository.GetAllCategories();
    foreach (var category in categoryies)
    {
        var products = repository.GetProducts(category);
        Console.WriteLine($"{category.Name}:{products.Count}");
    }
}

結構無駄なことをしてますが、あくまでサンプルということでご理解ください。 これを実行すると、以下のような出力が得られます。

本:3
家電:1
おもちゃ:1

C:\****\net6.0\ConsoleApp.exe (process 8988) exited with code 0.
Press any key to close this window . . .

これで正しく動作していることを確認できました。

まとめ

今回は Entity Framework Core を使って、コードからデータベースの構造を作り出し、データアクセスを行うまでの基本フローを解説しました。 次回は、今回のソリューションを使って、モデルクラスの修正と、データベースへの反映あたりの手順をまとめようと思います。

サンプルソースについて

本稿で解説したソースコード一式は、 GitHub で公開しています。

github.com

*1:MS サイトでは「移行」と訳されています

Markdown の linter を Azure Pipelines から実行する (GitHub Super-Linter を使う)

f:id:masatsuna:20210704195049p:plain

前回、 Azure Pipelines を使って Mkdocs を用いた静的サイトの生成を行いました。

tsuna-can.hateblo.jp

今回は、この Azure Pipelines の YAML をさらに拡張して、 Markdown ファイルの Linter を実行できるようにしていきます。

Linter とは

簡単に言うと、ソースコードフォーマットチェックを自動的に実行してくれるツールです。 特にチームで開発を行う場合、ソースコード開発時に、コードフォーマットのルールを定めることが良くあります。 そういったときに Linter を用いることで、ソースコードのフォーマットが開発者間で統一されているかどうかを自動的にチェックできるようになります。

Java の場合は CheckStyle .NET の場合は StyleCop など、Linter と呼ばれるものには、言語によってさまざまな種類があります。 今回対象とする Markdown の場合は、 Markdownlint が有名です。 Visual Studio Code の拡張機能 としても提供されていて、使い勝手も非常に良いものになっています。

チーム開発を行う場合、このような構文チェックを随時実行し続けることが非常に重要です。 随時実行する、ということは、 CI の中で構文チェックを自動的に実行し続けるようにしてあげる必要があります。 そうしないと、私のような怠惰な開発者は、故意かどうかは別として、フォーマット違反を無視してコミットしてしまうわけです。

Markdown の Linter を実行する手段

Mkdocs を利用すると、 MarkdownYAML ファイルを利用することになります。 チーム開発を考えたとき、これらのファイルについても Linter を適用したくなります。 そういったときに有用なツールが、 GitHub Super-Linter です。

github.com

このツールには、 GitHub Actions で実行するための様々な Linter が組み込まれています。 2021 年 7 月現在、 45 の言語に対応しています。

今回はこのツールを Azure Pipelines から実行して、 Markdown の Linter を実行しようと思います。

YAML の改造と実行

以前以下の記事で紹介した YAML を改造して、 mkdocs を実行する前に、 GitHub Super-Linter を実行するようにしていきます。

tsuna-can.hateblo.jp

GitHub Super-Linter は、 Docker 上で実行できるように、イメージが公開されています。 このイメージを Azure Pipelines 内で使うように構成し、GitHub Super-Linter を実行します。

pool:
  vmImage: ubuntu-latest

steps:
  - task: Bash@3
    displayName: 'GitHub Super-Linter によるスキャン'
    inputs:
      targetType: 'inline'
      script: |
        docker pull github/super-linter:latest
        docker run -e RUN_LOCAL=true -v $(System.DefaultWorkingDirectory):/tmp/lint github/super-linter

今回も前回同様、 ubuntu-latest のマシンを使用してパイプラインを実行します。 GitHub Super-Linter を実行するタスクは非常にシンプルで、上記のように docker イメージをプルして実行するだけです。 YAML をこのように設定して、 markdownlint で警告の出る状態の markdown をコミットし、パイプラインを実行してみます。

f:id:masatsuna:20210704174334p:plain

実行すると、このように markdownlint の警告が報告され、パイプライン自体がエラーで終了するようになります。 この例では、 markdownlint のルール MD025 に違反する Markdown ファイルをコミットしてみました。 しっかりその部分を認識して、エラーを通知してくれています。

GitHub Super-Linter の Docker イメージに関する詳細は、以下をご覧ください。

super-linter/run-linter-locally.md at master · github/super-linter · GitHub

環境変数を使って、いろいろ設定を変更することができます。 docker run コマンドに、 -e または -env オプションをつけると環境変数を設定することができるので、ほとんどのケースでイメージを再作成する必要はありません。

サンプルソースについて

本稿で解説したソースコード一式は、 GitHub で公開しています。

github.com

まとめ

今回は GitHub Super-Linter を利用して、 Markdown の Linter を Azure Pipelines から実行する方法について解説しました。 個別の Linter を準備するより手間なく適用できる点も魅力的なポイントですね。

また GitHub Super-Linter は、様々なファイル形式に対応しており、 Markdown 以外にもいろいろな Linter を一括で実行することができます。 Docker 上で実行できるため、ローカル開発環境でも利用できるメリットがあります。 うまく開発プロセスに組み込むことで、チーム開発に役立てることができそうですね。

ASUS BIOSでTPMを有効にする

f:id:masatsuna:20210627182844p:plain

以前 MSIBIOS で、 TPM を有効化する方法について解説しました。

tsuna-can.hateblo.jp

今回は ASUSBIOSTPM を有効化する方法について解説します。 TPM が有効になっているかどうかは、 [tpm.msc] を用いて確認できます。 手順は以前の記事で詳細に解説しているので、そちらを参照してください。

環境

さすがにこの世代の物はもうほとんど流通していないので、最新世代の同等品へ製品リンクを張っておきます。



BIOS 設定

今回も、日本語化した BIOS での設定例を解説します。

PC を起動したら、 [Del] または [F2] キーを連打して、 BIOS の画面に入ります。 以下のような画面になります。

f:id:masatsuna:20210627181943j:plain

ASUSBIOS は起動すると EZ Mode で起動するので、 [F7] キーを押下して、 [Advanced Mode] に切り替えます。 続いて、画面上部のメニューから [詳細] を選択します。 そしてメニューの一覧から [PCH-FW Configuration] を選択します。

f:id:masatsuna:20210627182110j:plain

[Intel Platform Trust Technology] の項目を [Enabled] に設定します。

f:id:masatsuna:20210627182252j:plain

設定が完了したら、 [F10] キーを押下して設定を保存し、 PC を再起動しましょう。 これで TPM 2.0 が有効になります。

おまけ

ついでなので Windows 11 がインストールできるかも確認してみましょう。 以前の記事 同様、 [PC 正常性チェック] のアプリで確認します。 インストーラーは、 2021/06/27 現在、以下からダウンロードできます。

このアプリは現在新しくなっています。 記事内の画像は初期バージョンのものですのでご注意ください。

https://aka.ms/GetPCHealthCheckApp

このアプリを実行すると、以下のような項目が表示されます。

f:id:masatsuna:20210625192906p:plain

[今すぐチェック] ボタンを押下すると、システム要件のチェックが自動的に行われます。 TPM を有効にした状態でこのチェックを行うと、以下のように Windows 11 のインストールができると表示されました。

f:id:masatsuna:20210625193008p:plain

この PC も安心して Windows 11 のリリースを待てそうです。

Windows 11 のインストール手順

実際に Windows 11 を VM にインストールしてみました。 その際の手順や、 TPM 無効時の挙動などはこちらからどうぞ。

tsuna-can.hateblo.jp

Hyper-V の仮想マシンで TPM を有効にする

f:id:masatsuna:20210626192531p:plain

前回は物理マシンで TPM を有効にする方法について解説しました。 今回はその物理マシン上に立てた Hyper-V仮想マシンで、 TPM を有効にする方法を解説します。

環境

今回の記事で使用した環境は以下の通りです。

物理マシン(ホストOS)



仮想マシン(ゲストOS)

  • VHDX 形式の仮想ハードディスク(容量は可変)
  • 第 2 世代仮想マシン
  • 起動メモリは16GB(動的メモリは無効)
  • Windows 10 Pro 21H1(TPMの設定状態確認のためだけに使用)

物理マシンの方には、以下のメモリを入れています。 今回仮想マシンのメモリもふんだんに確保していますが、普通そんなに準備する必要はありません*1


設定方法

まずは上記の仮想マシンを普通に作成します。 今回は OS のインストールまで先に実施してから、以下の作業を行いました。

仮想マシンの作成が完了したら、ゲスト OS を一度シャットダウンし、ホスト OS の Hyper-V 管理画面で作成した仮想マシンの [設定] 画面を開きます。

[設定] 画面の左側メニューで [ハードウェア] > [セキュリティ] を選択します。 そして以下のように、 [トラステッド プラットフォーム モジュールを有効にする] のチェックをオンにします。

f:id:masatsuna:20210626164336p:plain

この状態で [OK] ボタンを押下して、設定を保存します。 これで設定は終了です(簡単)。

動作確認

作成&設定した仮想マシンを起動します。 前回の記事で解説した通り、 [tpm.msc] を起動して、設定状態を確認します。

f:id:masatsuna:20210626191216p:plain

問題なく TPM 2.0 が認識されていることを確認できます。

おまけ

ついでなので Windows 11 がインストールできるかも確認してみましょう。 前回の記事 でも解説した [PC 正常性チェック] というアプリを利用して確認してみます。 インストーラーは、 2021/06/26 現在、以下からダウンロードできます。

https://aka.ms/GetPCHealthCheckApp

実行してみると、以下のように問題なし、となりました。

f:id:masatsuna:20210626191521p:plain

これで物理マシンを傷つける(?)ことなく Windows 11 をお試しすることができそうです。

雑記

それにしても Windows 11 は、 CPU の要件が相当に厳しいですね。 徐々に拡大していくのか、このままなのか、よくわかりませんが、ここ 3 年くらいの間にリリースされたものしかリストにありません。

docs.microsoft.com

docs.microsoft.com

docs.microsoft.com

偶然、我が家の PC は入れ替えたばかりだったので問題ありませんでしたが。。。 ビジネスユースの PC の場合、結構古い PC を使っているケースも多いのではないかと思います。 私も昨年夏まで Core 第 6 世代の PC を会社で使っていましたし、まだ同じものを使っている人もたくさんいます。 そう考えると、特にビジネスユースの PC 入れ替えニーズが大きく高まることが予想されます。 昨今の半導体不足解消は、もう少し先になってしまうかもしれませんね。

*1:Windows 11 では最低 4GB あればよいそうです