dotnet watchを使用した動的なWeb開発

.NET6から実行中にソースコードを変更すると、即時に動作が変更される、所謂ホットリロード機能が本格的に取り入れられた。

ロジックなどの変更にも対応しているが、Webアプリの画面デザイン等の変更⇒確認の流れが非常に楽になるので、お薦め。

Visual Studio 2022などでは、VSのUIからホットリロードモードに変更することが可能だが、これは裏でdotnet watchコマンドが実施されている。

Visual Studio Codeでホットリロードを行いたい場合は、dotnet watchコマンドでイメージを起動すれば同じようにプロジェクト中のソースコードなどが変更された場合、ホットリロードで画面やロジックなどが即座に変更される。(ホットリロード対応できない場合は再起動するかどうか聞いてくる)

下記はwebappテンプレートで作成されたIndex.cshtmlだが、dotnet runの代わりにdotnet watchコマンドで起動し、ソース(Index.cshtml)を変更しただけで、即座に画面に反映される。CSSファイルの内容変更などでも、同様である。

編集前
Index.cshtmlを編集して保存

特に、フォームやロジックを構築後にデザインのみを色々と試したい時などに便利。

ちなみに、この状態から、<form>タグを追加して、POSTハンドラを.cshtml.csに追加しても、再起動必要なく、反映させることができた。

うまく使うことによって、開発効率が上がるのではないかと思う。

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

JSONPの動的呼出

ブラウザのJavascriptからWeb APIを呼び出したいのだが、APIの提供先が、CORSに対応していないので、仕方なくJSONPで呼び出すことにした。(結構大手が提供しているサービスなのに・・・)

で、JSONPで動的なパラメータを使用(例えばMAP上で位置をクリックした場所の座標情報をパラメータと)して呼び出したいので調べたら、domにscriptタグを動的に追加するのが一般的なやり方のようだ。

しかし、イベント発生時にその都度追加して行くとどういう動作するか不安なので、作成するscriptタグは一つにしたい。

で、以下のようなコードを書いてみたら、うまく動きましたぜ。

var url = "https://・・・";
url += "&p1=" + param1;
url += "&p2=" + param2;
url += "&callback=callbackfunc";

// JSONPで呼び出すので、本体にscriptエレメントを追加してやる。

var scr = document.getElementById("JSONPScript");

if (scr != null) {  // すでに存在すれば削除
    document.body.removeChild(scr);
}
// 新たに追加
scr = document.createElement("script");
scr.id = 'JSONPScript';
scr.src = url;
document.body.appendChild(scr);

まぁ、JSONPはちょっと危険なのであまり使いたくは無いけど、PROXY使うと、遅くなるし、大手が提供しているAPIだから、まぁいいかなと・・・

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

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#, 技術系, 開発環境 | 1件のコメント

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#, 技術系 | 1件のコメント