.NET6 RC2での改悪?

.NET6 RC2がリリースされたので、.NET Blogに書かれている点などを確認してみた。

個人的に最悪だと思ったのは、Consoleプロジェクトテンプレートで、Mainが無くなっていたり、Web系プロジェクトテンプレートでMainや、Startup.csが無くなっている事。

やっぱり、C#なんだから、Mainは必要だと思うし、Web系もProgram.csに全て書くのはカッコ悪いと思う。(まぁ、個人的な意見だけど・・・、初心者はこれで覚えてしまうから、プログラムのエントリポイントはやはり、必要だと思うのだけれど・・・)

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

やっぱり、カッコ悪いよねぇ。

カテゴリー: .NET, C#, 技術系, 開発環境 | 1件のコメント

Windows11

10月になっていないのに、家のPCにWindows Update経由でWindows11の更新通知が来ていたので、アップデートしてみた。

システム情報

大したアプリを使っていないので、今のところ、とりあえず動作に不備は無い。

一つ気がついたのは、X-Serverを起動しなくても、WSLからX-Windowアプリを起動できること。これは、中々いいね。

カテゴリー: 日記的なもの | コメントする

Linuxのsystemdでworkerアプリを動かす

Linuxのsystemdでworkerアプリを動かして、systemctlコマンドで安全に開始・停止出来るようにするには、workerプロジェクトにMicrosoft.Extensions.Hosting.Systemdパッケージを追加して、Windowsサービスの時と同じように、UseSystemd()を追加する。

public class Program
{
	・・・
	public static IHostBuilder CreateHostBuilder(string[] args) =>
	    Host.CreateDefaultBuilder(args)
	        .UseSystemd()
	        .ConfigureServices((hostContext, services) =>
	        {
	            services.AddHostedService<Worker>();
	        });

以下のようなSystemd用のサービス定義ファイルを作成。

[Unit]
Description=Worker Test on Linux Systemd
[Service]
Type=notify
WorkingDirectory=<作業ディレクトリ>
ExecStart=/usr/bin/dotnet <DLLのPATH>
[Install]
WantedBy=multi-user.target

サービスタイプをnotifyとする。

後は、systemctlでサービスを登録して、起動・停止が可能。

systemctl enable <サービス名>.service
systemctl start <サービス名>
systemctl stop <サービス名>

実際に動かしてログをみてみると

MMM DD HH:mm:ss exodus-v12 systemd[1]: Starting Worker Test on Linux Systemd...
・・・
MMM dd HH:mm:ss exodus-v12 <サービス名>[nnnnn]: <プロジェクト名>.Worker[0] Worker running at: MM/dd/YYYY HH:mm:ss +09:00
MMM dd HH:mm:ss exodus-v12 <サービス名>[nnnnn]: <プロジェクト名>.Worker[0] Worker running at: MM/dd/YYYY HH:mm:ss +09:00
MMM dd HH:mm:ss exodus-v12 systemd[1]: Stopping Worker Test on Linux Systemd...
MMM dd HH:mm:ss exodus-v12 <サービス名>[nnnnn]: Microsoft.Hosting.Lifetime[0] Application is shutting down...
MMM dd HH:mm:ss exodus-v12 systemd[1]: <サービス名>.service: Succeeded.
MMM dd HH:mm:ss exodus-v12 systemd[1]: Worker Test on Linux Systemd.

systemctl stopを実行した時点でworkerプロセスに通知されている様子が分かる。

ただし、workerスレッドの実行ブロックループの後にログを出力させても、そのログが出力されていないので、workerスレッドの上位で終了処理を行っているように思える。

この辺りかな?
https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host

カテゴリー: .NET, C#, 技術系 | 1件のコメント

appsettings.jsonからの配列の取得

asp.net core系ではアプリケーション設定をappsettings.jsonに記述するが、ドロップダウンの値と表示名など配列を記述したい場合もあると思う。(↓参照)

{
  ・・・
 "dropdownValues" : [
    { Value = "X01", Name="Selection1" },
    { Value = "Y01", Name="Selection2" },
    { Value = "Z01", Name="Selection3" }
  ]
}

このように定義されたデータを配列またはListとして取得するにはどのようにしたら良いかというと、以下のように取得すれば良い。(※listItemクラスは別途用意しておく)

// confはコンストラクタで受取ったIConfiguration
var dlst = conf.GetSection("dropdownValues").Get<List<listItem>>();

ちょっと、戸惑ったのでメモ。

カテゴリー: .NET, asp.net core, C#, 技術系 | コメントする

dotnet core系からのSOAPアクセス

SOAPを使用したWeb APIって、最近はあまり見かけないけど、まぁ、残っているところには残っていて、それを使いたかったので、dotnet core系から使うにはどうすれば良いかを調べてみた。

.NET Frameworkの頃はVisual Studioを使って、サービス参照から追加すれば簡単にできたのだが、dotnet core系でCLI環境ではそういうわけにはいかない。で、調べてみたら、svcutilを使って、WSDLからアクセス用のクラスとデータクラスを自動生成してくれるようだ。svcutilは.NET Frameworkからあったけど、dotnet core系の場合、toolとして、dotnet-svcutilが提供されている。まず、このツールをインストールしよう。

dotnet tool install dotnet-svcutil -g

で、以下のようにして、WSDLからアクセス用とデータ用のクラスを生成する。

dotnet svcutil <WSDLのURL>

オプションを何も指定しないと、実行したフォルダの下にServiceReferenceと言うフォルダが作成され、namespace ServiceReference中にアクセス用のクラスとデータクラスが作成される。オプション指定でフォルダ名やnamespaceの指定も可能。

プログラムから、サービスにアクセスするためには、プロジェクトに下記のパッケージを追加する必要がある。

  • System.ServiceModel.Primitives
  • System.ServiceModel.Http

プログラム中からSOAPサービスに接続するには、以下のようにサービスアクセス用のインスタンスを作成してメソッドを呼び出す。(認証等を使用しなければならない場合はbindingや認証設定などが必要)

using System.ServiceModel;
using ServiceReference; // svcutilでnamespaceを指定した場合はそのnamespace
・・・
// サービスアクセス用インスタンスの作成
var cli = new <サービス名>Client();
// サービスのメソッドを呼び出す
var result = await cli.<メソッド名>Async(<パラメータ>);

using System.ServiceModel;
using ServiceReference; // svcutilでnamespaceを指定した場合はそのnamespace
・・・
// GetUserService SOAPクライアント
// ※認証なし
var cli = new GetUserServiceClient();

// ユーザー番号00001番のユーザーを取得
// ※Userクラス定義はWSDLから自動的に生成される
User ret = await cli.GetUserAsync("00001");

以上、忘れないようにメモ。

カテゴリー: .NET, C#, 技術系 | コメントする

LINQでLEFT OUTER JOIN

LINQで外部結合(LEFT OUTER JOIN)を行うためのメモ。

LINQで内部結合(LEFT INNER JOIN)を行うには、JOIN句を使って、以下のような感じで行えば良いのは良く例も出ているし、構文上も素直に納得できる。
(ちなみに、複数キーがある場合は、
new {t1.key1,t1.key2[,…]} equals new {t2.key1,t2.key2[,…]}
の形で指定する)

// Collection1とCollection2の[KEY]が同じものを結合
// 同じキーを含まないCollection1は集合から外される。
var q = from t1 in Collection1
	join t2 in Collection2 on t2.[KEY] equals t1.[KEY]
	・・・

では、外部結合(LEFT OUTER JOIN)、つまり、Collection1は全てのレコードが選択され、Collection2が存在しない場合は、t2をNULLとする)にはどのように表現するのが一番簡単か?

答えは意外なもので、以下のようなクエリを用いることでかなり簡単に実装可能である。

// Collection1とCollection2の[KEY]が同じものを結合
// ただし、Collection2と同じキーを持っていないCollection1も集合に含める
var q = from t1 in Collection1
		from t2 in Collection2.Where(c=>c.[KEY] == t1.[KEY]).DefaultIfEmpty()
	・・・

1つのクエリ中にfrom句を2回使用して、2つ目の集合は1つめのキーで絞込をかけ、さらに、そのキーを持つレコードが存在しない場合にはデフォルト値(null)を返すようにする。

つい最近まで、from句を2回以上、使用できるの知らなかった・・・

カテゴリー: .NET, C#, LINQ, 技術系 | コメントする

MBRからGPTへの変換

今秋位にWindows11がでるらしいので、自宅のPCでWindows11が動作するか、MS.謹製のツールで確認してみたところ、NGとなった。

4年前ぐらいに作ったPCで、Intelの第8世代Core i7だったので、行けると思っていたのだが・・・

MS.謹製のツールでは何が悪いか細かく出てこないので、このツールを使ってチェックしてみたところ、BootがBIOSモードでGPTのボリュームが存在していないと表示された。(それ以外はOK)

原因はBIOS設定のCSM有効のまま、Windowsをインストールしたためのようだ。デフォルトがそうなっていたかどうかは忘れたが、そのせいでWindowsはMBRモードでインストールされていた。

それじゃぁ、MRRをGPTに変換してみようということで、変換にチャレンジ。

まずは、EaseUS(商用版)でチャレンジ。PEイメージを作成して、DVDからブート後、イメージバックアップを取ってから、GPTへの変換を行ってみた。途中まではうまく変換されているようだが、「不明なエラー」が表示されてNG。

仕方ないので、他の方法を探してみる。

ググってみると、mbr2gptというMS謹製のコマンドがあるらしい。

まず、GPTに変換できるかをチェックする。

> mbr2gpt /validate /disk:1 /allowFullOS
MBR2GPT: Attempting to validate disk 1
MBR2GPT: Retrieving layout of disk
MBR2GPT: Validating layout, disk sector size is: 512 bytes
MBR2GPT: Validation completed successfully
>

/disk:n でディスク番号を指定する(上記はバックアップした媒体に対してチェックしてみた)。

最後に「MBR2GPT: Validation completed successfully」と表示されたら、変換可能。

で、バックアップしてあるので、早速変換をかけてみた。

> mbr2gpt /convert /disk:0 /allowFullOS

すると、以下のようなエラーが・・・

MBR2GPT: Failed to update ReAgent.xml, please try to manually disable and enable WinRE

で、またまたググってみると、こんな動画が見つかった(英語です)。

見進めていくと、同じエラーが出た後、リブートして、BIOS設定でCSMを外してUEFIブートに変更(こればかりはBIOSメーカーによって違うから、自分で項目を探さないとだめだが)後、保存してWindowsを起動する。

ちゃんと再起動ができれば、GPTへの変換は完了している。

もしかしたら、EaseUSのエラーもこれだったのかも。動いているから、もう一度確かめるつもりはないが、他のPCで試してみようか。

で、オープンソースの互換性チェックをかけたら、ALL Green。ついでに、MS.謹製のツールでもチェックしてみたら、Windows11にアップグレード可能と出ましたとさ。

カテゴリー: Windows, 技術系 | コメントする

ラズパイの開発環境

ラズパイで遊んでいるのだが、開発環境についてちょっと書いておこう。

ラズパイ本体は3B+で、ケースなどのキットとして購入。OSはこのキットに付いてきた、MicroSDに入っていたデフォルトのRaspberry Pi OS(32bit)。

最初だけ、HDMIでディスプレイとつなげ、USBキーボードとUSBマウス繋いで、無線LANのセットアップを行った。(GUIで超簡単にできたよ。)

無線LANで固定IPにした後は、WindowsからSSHで接続し、TeratermやWindows Terminal等で操作。

開発用として.net 5.0 SDKのARM32バイナリ版とVScodeを導入。

開発は、dotnet cliでプロジェクトを作成した後、Windows上のVSCodeのRemote-SSH Extensionを使用して、フォルダを開きコードをエディットと言う形で行っている。

ラズパイ3B+はメモリが1G Byteしかないので、ラズパイ上のGUIで行うよりこちらの方が、速いと思う。

ちなみに、C# Extension(OmniSharpベース)はこのバージョンのArm版Linuxに対応していないので、デバッグするにはリモートでバッグ手法をとらなければならない。(それほど複雑なプログラムを組むわけじゃないからデバッガ使わないけど・・・)

メモリ1G Byteなので、毎回ビルドすると、それなりに時間がかかるので、作成したプログラムを動作させる場合は、dotnet buildでビルドしておいてから、実際に動かすときは、dotnet run –no-buildオプションを付けて再ビルドしないで、実行させている。

なお、電子回路の動作はConsoleプロジェクトを作って確認し、確認後に、Blazor ServerでUIを持つアプリを作って動かしている。

とまぁ、こんな感じです。

カテゴリー: C#, IOT, 技術系 | コメントする

ラズパイ+.net 5でIOTもどき

ついに、ラズパイに手を出してしまった・・・

ちょっと、マシンルームの温度計測やメールによるアラート機能を付けたくて、とりあえず、ラズパイ(3B+)と入門キットを購入。

電子回路なんて、高校の時にイジって以来だから、全然分からないけど、とりあえず、入門書とか読みながら、簡単な回路を作ってお試し中。

入門キットのサンプルは、CやPythonなんかで書かれているけど、やはり、ここはC#で書きたいよね。

で、ラズパイに.net 5を導入。ラズパイ3B+はARM64なんだけど、デフォルトで入っている、OSは32bit(メモリ1Gだからまぁ妥当だろう)のDebianベースのOSだが、デフォルトではapt listで調べるも、dotnet-sdkは見つからない。何かしらの方法はあるのだろうが、パッケージマネージャーからインストールするのは諦めて、.net 5ダウンロードサイトからのarm32版のtar.gzを落としてきて、展開。(どうせ、複数人数で使うわけじゃないから問題無し)

で、簡単な回路を作成してみる。

回路

とりあえず、赤枠で囲んだ部分が最初に作った回路。簡単に書くと以下のような回路になっている。

青いケーブルが+3.3Vに刺さっていて、赤いケーブルはラズパイ上のGPIO0に刺さっている。GPIO0の後の()に17と書かれているのはBCMと呼ばれる番号で、Microsoft製のライブラリはこちらの番号を指定する必要がある。以下はgpioコマンドの出力で、いくつかの名前やピン番号が表示されている。ライブラリによって、使用するピン番号が違うので、注意されたい。

ラズパイGPIO

なんで複数の呼び方があるのかとか言うのは、ラズパイ入門編のサイトとかに必ず書いてあるので、そちらを参照して欲しい。

で、今回この回路でやりたいのは、LEDの点灯・消灯である。回路を見ると、GPIO0(BCMピン番号17)が邪魔をする形になっているので、LEDを点灯させたければ、GPIO0(BCMピン番号17)の電位を落とす必要がある。逆に消灯させたい場合は電位を上げれば良い。

ということで、以下のようなコードでプログラムからLEDのオン・オフが実現できる。
(プロジェクトに↓のパッケージ追加が必要)

  • System.Device.Gpio
  • Iot.Device.Bindings

プログラムは単純にConsoleアプリで作ってみた。

using System;
using System.Threading;
// GPIOライブラリ
using System.Device.Gpio;

namespace oneLedCtrl
{
    class Program
    {
        static void Main(string[] args)
        {
            const int pinno = 17;   // BCM Pin No
            // GPIOコントローラインスタンス作成
            var ctrl = new GpioController();
            // Pinのオープン(書き込みモード)
            ctrl.OpenPin(pinno,PinMode.Output);
            PinValue val = PinValue.High;
            // 1秒ごとに点滅を繰り返す
            for(;!Console.KeyAvailable;) {
                if (val == PinValue.High) {
                    val = PinValue.Low; // 点灯
                } else {
                    val = PinValue.High; // 消灯
                }
                // GPIO0(BCM 17)に書き込み
                ctrl.Write(pinno,val);
                Thread.Sleep(1000);
            }
            ctrl.ClosePin(pinno);
        }
    }
}

これくらいの回路なら、簡単だからいいんだけど、複雑なのは配線も面倒だよね。
業務で使うのなら、やっぱり、回路が組んである製品を購入すべきなんだろうね。

まぁ、とりあえずは、入門キットで色々と遊んでみるつもり。

カテゴリー: C#, IOT, raspberry pi, 技術系 | 1件のコメント

知っている人には当たり前かもだけど・・・

asp.net core webappのページハンドラはデフォルトでは、GET要求はOnGet,POST要求はOnPostによって処理されるが、View側から実行するハンドラを変更することが可能。

方法は、TagHelperを使用して、Submit時にasp-page-handler属性を指定する。

例えば、以下のような感じで、Submitボタンによって呼ばれるハンドラを指定することが可能。

<form method="post">
	<button class="btn btn-secondary" asp-page-handler="cancel" type="submit">Cancel</button>  
	<button class="btn btn-primary" asp-page-handler="ok" type="submit">OK</button>
</form>

このような指定をすると、Cancelボタンクリック時には、OnPostCancelが呼び出され、OKボタンクリック時にはOnPostOkが呼び出される。ちなみに、ハンドラはOnの後ろに、asp-page-handlerで指定された名前の最初が大文字であれば、その後ろは大文字でも、小文字でも大丈夫なようだ。(OnPostOKでもOnPostOkでも大丈夫⇒静的言語なのに・・・)

なお、asp-page-handlerを指定した場合、URLに?handler=<ハンドラ名>が付加される。

URLを勝手に変更されたり、動的言語的な判断されるので、個人的には、好きな方法じゃないけど・・・

カテゴリー: asp.net core, dotnetcore, 技術系 | コメントする