C#10 ファイルスコープnamespace

C#10では、1ファイルに1つのネームスペースを記述する場合、下記のように、namespaceを{}でくくる必要が無くなった。

// C#9以前
namespace XXXX.Pages {
	public class IndexModel : PageModel
	{
		・・・
	}
}
// C#10以降
namespace XXXX.Pages;

public class IndexModel : PageModel
{
	・・・
}

これは見た目にも分かりやすいし、プロジェクトテンプレートもこういう形でソースを生成してくれるのだが、dotnet-ef等、一部のツールで生成されるソースは未だC#9以前の形で生成されてしまう。早く統一してくれないかな・・・

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

.NET6 Blazor selectタグ multiple @bind

なんか、呪文みたいなタイトルになってしまったが、言いたいことは、Blazorが.NET6から、複数指定(multiple)selectのbindをサポートしたというお話。

どういうことかというと、以下の例を見て頂ければわかると思う。

@page "/"

<h2>Multiple Select Bind</h2>

<select style="width:200px" multiple size="6" @bind="SelectedColors">
	<option value="red">赤</option>
	<option value="blue">青</option>
	<option value="green">緑</option>
	<option value="magenta">紫</option>
	<option value="yellow">黄</option>
	<option value="orange">橙</option>
</select>
<br/>
@foreach(var c in SelectedColors) {
	<div style="width:100px;height:30px;background-color:@c"></div>
}

@code {
	protected string[] SelectedColors = new string[] {};
}
実行結果

なお、配列のサイズは動的に変更される。つまり、上記の例では、SelectedColors.Length=3だが、下記の実行例では、SelectedColors.Length=2となる。

実行例2

検索とかで、複数カテゴリなどを選択するときなどに結構便利。

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

.NETプロジェクトでDLLの参照

.NETプロジェクトで自社製DLL等を直接プロジェクトから参照したい場合などがある。

dotnet cliで出来ないかな~と思って調べたけど、nugetパッケージやproject参照は可能だが、DLLの直接参照は出来ないらしい。

いろいろ調べたが、以下のような感じで、プロジェクトファイル(C#なら.csproj)に手動で追加するしかないようだ。

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

	・・・

    <ItemGroup>
	    <Reference Include="jp.co.hoge.mydll"><!-- ネームスペース -->
	        <SpecificVersion>False</SpecificVersion><!-- フレームワークバージョン特化? -->
	        <HintPath>/usr/share/sharelib/mydll.dll</HintPath><!-- アセンブリの在処 -->
	    </Reference>
	</ItemGroup>

</Project>

面倒だから、dotnet cliから参照追加できるようにして欲しいな・・・

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

global using

C# 10から、global usingが使えるようになり、どのファイルでも使用するようなネームスペースは各々のファイルでusingを記述することなく、.csprojで定義することが可能となった。

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings><!-- 暗黙的なusingを使用(デフォルト) -->
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <Using Include="System.Net.Sockets" /><!-- global usingの追加 -->
  </ItemGroup>

</Project>

ImplicitUsingsがenableの場合、暗黙的に下記のようなglobal usingが定義される。

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

使い方によっては、それなりに便利だと思うけど、見えないところで勝手にジェネレートする設定をデフォルトにするのはやめて欲しいな・・・

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

.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, 技術系 | コメントする