F¶
注意: .NET
とC#
も含む¶
- はじめに
F#
でカテゴリを作ってしまった. - わけるのも今更別立てするのも面倒なため全部ここにまとめる.
ガイド¶
.NET¶
GitHub¶
Description | Link |
---|---|
C# language design | https://github.com/dotnet/csharplang |
Compiler implementation | https://github.com/dotnet/roslyn |
Standard to describe the language | https://github.com/dotnet/csharpstandard |
コマンド¶
バージョン¶
dotnet --version
Emacs連携¶
FSAutoCompleteが導入できないとき¶
- F#のファイルを開いたときに次のようなメッセージが出るとき
コマンドが誤っています
dotnet-path/to/.emacs.d/.cache/lsp/fsautocomplete/fsautocomplete.dllが見つかりません.
/tmp
や~/AppData/Local/Temp
にfsautocomplete1HbFmC.zip
のようなファイルがダウンロードできていたので, これを~/.emacs.d/.cache/lsp
に展開.- もう一度F#のファイルを開いてLSPが発動するか確認
FsAutoComplete¶
- LSPからの自動インストールによく失敗する.
- 直接公式からクローンしてビルドする.
- 直下の
global.json
とdotnet
のバージョンを合わせること. dotnet tool restore
でfake
を入れてからdotnet fake build
.src/FsAutoComplete/bin/Release/net5.0
にビルドされている- これを
~/.emacs.d/.cache/lsp/fsautocomplete
にコピー
- これを
fsi¶
- fsiでライブラリを使いたいときの対応
dotnet fsi scriptname
で実行できる.- 閉じるには
#quit;;
. - VSCodeでREPLをゴリゴリ使うのがよい.
Macでの調整¶
EmacsでのFSACエラー¶
cat /etc/dotnet/install_location
/usr/local/share/dotnet/x64
->
/usr/local/share/dotnet
trydotnet, ifsharp, Jupyter notebook with .NET Core¶
Try .NET, Jupyter notebook with .NET Core¶
要調査: これ用にインストールしたパッケージは ~/.trydotnet に置かれる?
- https://devblogs.microsoft.com/dotnet/net-interactive-is-here-net-notebooks-preview-2/
- https://github.com/dotnet/interactive
- https://qiita.com/yshr10ic/items/a0ec264efe475039fe56
dotnet tool install --global Microsoft.dotnet-interactive
dotnet interactive jupyter install
jupyter kernelspec list
IFsharp へのパッケージ導入¶
trydotnet でも同じ?
まとめ¶
#load "Paket.fsx"
Paket.Package ["Newtonsoft.Json"; "Deedle"; "MathNet.Numerics"; "MathNet.Numerics.FSharp"; ]
#load "Paket.Generated.Refs.fsx"
open System
open Deedle
open MathNet.Numerics
open Newtonsoft.Json\
open MathNet.Numerics.LinearAlgebra
load "XPlot.Plotly.Paket.fsx"
load "XPlot.Plotly.fsx""
open FSharp.Core
open XPlot.Plotly
// 以下はF# 5以降の方法:いったんコメントアウト。
//#r "nuget:Deedle"
//#r "nuget:DiffSharp"
//#r "nuget:FSharpPlus"
//#r "nuget:MathNet.Numerics"
//#r "nuget:MathNet.Numerics.FSharp"
//#r "nuget:XPlot"
ツイートからのやりとり¶
緩募 IFsharp で DiffSharp を使う方法. そもそもどうやって導入すればいいのかさえわからない. Powershell で dotnet add package DiffSharp --version 0.7.7 と打ったら「プロジェクトが見つかりませんでした」とか言われて怒られる. Install-Package でもうまくいかない. つらい.
そもそも .NET には一般のプログラミング言語と違いグローバルなライブラリ環境が存在しない, ということに注意が必要です. では dotnet add package や Install-Package は何をするコマンドなのかというと, 単一のプロジェクト (*.fsproj) に依存パッケージを追加するコマンドです.
一方 IFSharp は F# interactive の上に構築されているのでプロジェクトが存在しません. ではどうすればよいのかというと, ここに記載されているように
load "Paket.fsx"¶
としてから Paket.Package もしくは Paket.Version (バージョン指定) に使いたいパッケージ名を渡し,
load "Paket.Generated.Refs.fsx"¶
とすることでパッケージを IFSharp の環境にインストールすることができます. 初回実行時にはパッケージの依存解決とダウンロードが走るので処理に時間がかかります. また Paket を使うセルは他の処理と分けておいた方がよいです (補完を利かせるため).
vscode連携¶
ツールチップなどが出てこない¶
- F#用のディレクトリだけを開けばよさそう
やたらエラーが出る¶
- 参考
- エラーを出したディレクトリに
.nuget/packages/
へのシンボリックリンクを張れば良い(?) - 自分用参考: コマンドプロンプトで
mklink myhome\junk\fsharp\packages myhome\.nuget\packages
myhome
ホームディレクトリへのフルパス: 使うマシンごとに切り替えよう.- Windows のコマンドプロンプトだから, ディレクトリの区切りは
\
で.
Windowsでの英語化¶
- 参考: URL
- Visual Studio Installerで適当に設定を眺めてみる
アップデート¶
- 2022/12時点では公式からバイナリインストールするしかない模様.
アンインストール¶
Mac¶
/tmp直下¶
事前にバージョン(curlでのダウンロード先)を確認すること.
cd /tmp
curl -OL https://github.com/dotnet/cli-lab/releases/download/1.5.255402/dotnet-core-uninstall.tar.gz
mkdir -p /tmp/dotnet-core-uninstall
tar -zxf dotnet-core-uninstall.tar.gz -C dotnet-core-uninstall
cd dotnet-core-uninstall
dotnet-core-uninstall list
./dotnet-core-uninstall -h
~/tmp直下¶
事前にバージョン(curlでのダウンロード先)を確認すること.
cd ~/tmp
curl -OL https://github.com/dotnet/cli-lab/releases/download/1.5.255402/dotnet-core-uninstall.tar.gz
mkdir -p ~/tmp/dotnet-core-uninstall
tar -zxf dotnet-core-uninstall.tar.gz -C dotnet-core-uninstall
cd dotnet-core-uninstall
dotnet-core-uninstall list
./dotnet-core-uninstall -h
インストール¶
Windows¶
- Build Tools for Visual Studio 2019
- 適宜最新版を選ぶ
- インストール対象を選ぶ画面では「.NET デスクトップ ビルドツール」をチェック
- 右側に表示されるオプションで「F# コンパイラ」をチェック
- パス指定
- C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\FSharp
M1 Mac¶
- ビルド時に必要なことがあり, Arm版だけではなくx64版もインストールする.
Linux¶
- Use F# on Linux
- Debian(Chromebook上のLinux)にはDebian版があるので, 適切なOSでのインストール法を選ぶこと.
- 細かい記述は変わるのでここではコマンド詳細は記録しない: 公式情報を確認すること.
dotnet-install.shを使うとき¶
- バージョン指定は
--channel
と--version
がある.- 適切な方を使うとよいが2022-02-09のChromebook作業時はFsAutoCompleteなどの関係で
--version 6.0.100
を利用 dotnet tool restore
- dotnet fake buildできるようになる.
- 適切な方を使うとよいが2022-02-09のChromebook作業時はFsAutoCompleteなどの関係で
プロジェクト作成¶
VSCodeを前提にする.
- 下記の内容のファイルを
setup.txt
として作る.MySolution
とMyProject
は適宜修正.Ctrl+F2
で一斉置換できる.
Ctrl+Shift+P
でコマンドパレットを開いて`TRSTAT と打って実行.
setup.txt¶
dotnet new sln -o MySolution
cd MySolution
mkdir src
dotnet new console -lang F# -o src/MyProject
dotnet sln add src/MyProject/MyProject.fsproj
mkdir tests
dotnet new xunit -lang F# -o tests/MyProjectTests
dotnet sln add tests/MyProjectTests/MyProjectTests.fsproj
cd tests/MyProjectTests
dotnet add reference ../../src/MyProject/MyProject.fsproj
dotnet add package FsUnit
dotnet add package FsUnit.XUnit
dotnet build
dotnet test
記事集¶
公式情報¶
よくまとまっていて参考になる¶
- cannorin さん のQiita まとめ
- 自分の GitHub レポジトリ
- チートシート
- Guide for C# devs to learn F# real FAST
- fsugjp wiki
- F# Fun and Profit
MISC¶
読書メモ, 2022 MASTERING MINIMAL APIS IN ASPNET CORE¶
1章¶
dotnet --list-sdk
2章¶
- P.38
dotnet tool install -g LiveReloadServer
- P.39, OpenAPIサポートのためにNuGetの
Swashbuckle.AspNetCore
がある
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
3章¶
- P.58,
options pattern within an ASP.NET application
は何か? - P.59,
.NET Core
から設定ファイルはweb.config
からappsettings.json
になった. - P.59,
IConfiguration
で与えられたオブジェクトでappsettings
の設定を読める. - P.59, 他には次の対象から設定を取得する
- 参考
- Environment variables
- Azure Key Vault
- Azure App Configuration
- Command-line arguments
- Custom providers, installed or created
- Directory files
- In-memory .NET objects
- P.71
docker-compose.override.yaml
ASP.NETかつM1 Macでのキーチェーン要求を解除する¶
- URL
- 単純にPCログイン用のパスワードを入力し, ダイアログ内の「常に許可」を選択すればよい.
ASP.NET 開発中にhttpsを使う¶
dotnet dev-certs https --clean
dotnet dev-certs https --trust
ASP.NET 初期化¶
- 必要に応じて
SeedData.cs
も生成しておく
dotnet restore
dotnet tool restore
ASP.NET 導入メモ: global.json
¶
- バージョンを適切に設定すること.
{
"sdk": {
"version": "6.0.400"
}
}
ASP.NET 導入メモ: docker-compose.yml¶
- バージョンは都度修正すること.
.env
は開発サンプル用設定として共通にしている: 必要に応じて適切な値を設定すること.
PORT=3000
DB_ROOT_HOST="%"
DB_ROOT_USER=postgres
DB_ROOT_PASS=root
POSTGRES_USER=user
DB_USER=user
DB_PASS=pass
DB_PORT=5432
DB_NAME=mydb
TZ=Asia/Tokyo
version: "3"
services:
pgsql:
image: postgres:14
platform: linux/x86-64
tty: true
env_file: .env
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
TZ: ${TZ}
ports:
- ${DB_PORT}:5432
# volume:
# - ./db/init:/docker-entrypoint-initdb.d
healthcheck:
test: [ "CMD", "pg_isready", "-U", "${POSTGRES_USER}", "||", "exit", "1" ]
interval: 2s
timeout: 5s
retries: 5
ASP.NET 導入メモ: docker-compose.with-dotnet.yml¶
- 参考:Docker Compose and Multiple Containers, Mount The Development Directory
- TODO: 設定は
Dockerfile
に移行したいがよくわかっていない. 開発用のDocker
設定としてとりあえずよしとする.
- TODO: 設定は
- バージョンは都度修正すること.
.env
は開発サンプル用設定として共通にしている: 必要に応じて適切な値を設定すること.
PORT=3000
DB_ROOT_HOST="%"
DB_ROOT_USER=postgres
DB_ROOT_PASS=root
POSTGRES_USER=user
DB_USER=user
DB_PASS=pass
DB_PORT=5432
DB_NAME=mydb
TZ=Asia/Tokyo
version: "3"
services:
pgsql:
image: postgres:14
platform: linux/x86-64
tty: true
env_file: .env
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
POSTGRES_DB: ${DB_NAME}
TZ: ${TZ}
ports:
- ${DB_PORT}:5432
healthcheck:
test: [ "CMD", "pg_isready", "-U", "${POSTGRES_USER}", "||", "exit", "1" ]
interval: 2s
timeout: 5s
retries: 5
backend:
image: "mcr.microsoft.com/dotnet/sdk:6.0"
volumes:
- ./Database:/app
command: dotnet watch --project ./app run --urls "http://0.0.0.0:80"
ports:
- "80:80"
ASP.NET 導入メモ: ツールのインストール¶
- ローカルインストールの
EF Core
はdotnet dotnet-ef
コマンドで呼び出す - バージョンは適宜最新化すること
dotnet new tool-manifest
dotnet tool install dotnet-ef --version 6.0.12
dotnet tool install Microsoft.Web.LibraryManager.Cli
EF Core
をグローバルインストールしていて, グローバルのツールのバージョンを合わせたい場合は次のコマンドを発行する.
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef --version 6.0.12
ASP.NET 導入メモ: パッケージのインストール¶
dotnet add package Microsoft.EntityFrameworkCore --version 6.0.12
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 6.0.12
- 必要に応じて
EF Core
は次のパッケージを利用
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 6.0.12
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 6.0.8
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.EntityFrameworkCore.SqlServer -v 6.0.12 # scaffoldで必要
- 必要に応じて次のパッケージを利用
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design -v 6.0.11
ASP.NET 導入メモ: Program.cs
への各データベース設定¶
SQLite¶
builder.Services.AddDbContext<MyDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("MyDbContext") ??
throw new InvalidOperationException("Connection string 'MyDbContext' not found.")));
In Memory¶
builder.Services.AddDbContext<MyDbContext>(opt =>
opt.UseInMemoryDatabase("mydb"));
PostgreSQL¶
builder.Services.AddDbContext<MyDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("MyDbContext") ??
throw new InvalidOperationException("Connection string 'MyDbContext' not found.")));
ASP.NET 導入メモ: フロントエンド用ライブラリ導入, LibMan
でのインストール¶
- MacでLibManを使うときは
export PATH="$PATH:/Users/username/.dotnet/tools"
を設定すること. - これでうまくいかない場合は次の通り.
dotnet new tool-manifest
dotnet tool install Microsoft.Web.LibraryManager.Cli
dotnet libman
で実行
LibMan (Microsoft.Web.LibraryManager.Cli)
は(ローカル)インストール済みとする.wwwroot/lib
をリネームする.
dotnet libman init -p cdnjs
dotnet libman install bootstrap --provider cdnjs --destination wwwroot/lib/bootstrap
dotnet libman install jquery --provider cdnjs --destination wwwroot/lib/jquery
dotnet libman install jquery-validate --provider cdnjs --destination wwwroot/lib/jquery-validation
dotnet libman install jquery-validation-unobtrusive --provider cdnjs --destination wwwroot/lib/jquery-validation-unobtrusive
dotnet libman install @fluentui/web-components --provider cdnjs --destination wwwroot/lib/fluentui/web-components
View
の参照を正す.
ASP.NET 導入メモ: データベースのスキャフォールド¶
- データベース側の準備一例
A5SQL
を使って作ったER図doc/mydba5er
からSQLを生成してdb/init/init.sql
に置く- SQLで
ID
をId
に置換する - データベースを初期化:dockerを立ち上げれば自動的に初期化される
- 再作成したい場合は
docker compose down
してからdocker compose up --build
- 再作成したい場合は
TODO
:接続文字列のセキュリティに関連してシークレットマネージャーツールを使うべしと怒られる- cf. スキャフォールドのオプション
dotnet dotnet-ef dbcontext scaffold 'Host=localhost;Database=mydb;Username=user;Password=pass' Npgsql.EntityFrameworkCore.PostgreSQL --output-dir Models --context-dir Context --context MyDbContext
ASP.NET 導入メモ: コントローラーのスキャフォールド¶
Rider
(またはVisual Studio)のコントローラースキャフォールドで対応- 実質的には
dotnet-aspnet-codegenerator
を発行している - 具体的には次のようなコマンドを発行する
- 実質的には
dotnet tool install dotnet-aspnet-codegenerator --version 6.0.11
dotnet dotnet-aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers
ASP.NET 導入メモ: MVCとAPIの共存¶
- MVCを基礎に構築しているとする
dotnet add package Swashbuckle.AspNetCore
を導入するProgram.cs
に追記する
// API利用:`Swashbuckle.AspNetCore`が必要
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ToDo API",
Description = "An ASP.NET Core Web API for managing ToDo items",
TermsOfService = new Uri("https://phasetr.com/archive"),
Contact = new OpenApiContact
{
Name = "Example Contact",
Url = new Uri("https://phasetr.com/contact")
},
License = new OpenApiLicense
{
Name = "Example License",
Url = new Uri("https://phasetr.com/archive")
}
});
});
// 中略: `var app = builder.Build();`のあと
// API利用:開発時は`Swagger`を立ち上げる
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// API利用
app.MapControllers();
- コントローラーには次のディレクティブを追加する
using Microsoft.AspNetCore.Mvc;
using HogeProject.Models;
namespace HogeProject.Controllers.Api.V1;
// 次のディレクティブの追加: これでAPI呼び出しでき, `Swagger`にも反映される
[ApiController]
[Route("api/v1/[controller]")]
public class HogeController : ControllerBase
{
// 本体
}
ASP.NET 導入メモ: EF Core
のマイグレーション¶
- 下記の「EF Core マイグレーション」を参考にすること.
ASP.NET Program.cs
, WebApplicationBuilder
¶
var builder = WebApplication.CreateBuilder(args);
IApplicationBuilder
: アプリケーションのリクエストまたはミドルウェア パイプラインの構成を許可IEndpointRouteBuilder
: 受信リクエストを特定のページにマッピングする構成を有効化IHost
: アプリケーションを開始および停止する手段を提供
ASP.NET Program.cs
, WebApplicationBuilder
のプロパティ¶
Environment
: アプリケーションが実行されているWeb
ホスティング環境に関する情報を提供Services
: アプリケーションのサービスコンテナを表すbuilder.Services.AddRazorPages();
Configuration
: 構成プロバイダーの構成を有効化Logging
: ILoggingBuilder経由でロギング構成を有効化Host
: サードパーティのDI
コンテナーを含むアプリケーションホスト固有のサービスの構成をサポートWebHost
Webサーバー構成を有効化builder.WebHost.UseWebRoot("content");
ASP.NET Blazor: フロントに送る容量をおさえつつBlazorを使う¶
- 参考
Blazor Server
を使い,SignalR
による通信を潰す- 規定で初回のサーバーサイドレンダリングは入る
_Host.cshtml
の修正_framework/blazor.server.js
を読み込むscript
タグを削除する- タグヘルパー
component
の属性指定でrender-mode
をStatic
に書き換える
Program.cs
の修正services.AddServerSideBlazor();
を削除app.UseEndpoints
内のendpoints.MapBlazorHub();
を削除- 2023/01時点では
app.MapBlazorHub();
か?
- 2023/01時点では
ASP.NET Razor Class Library (RCL)¶
- URL
- 標準では
~/Areas/Pages
に置く ~/Pages
でコンテンツを公開するRCL
を作成する場合は上記ページの「RCL
ページのレイアウト」参照RCL
でRazor Pages
を使う場合はホスティングアプリでRazor Pages
サービスとエンドポイントを有効化する
builder.Services.AddRazorPages();
app.MapRazorPages();
ASP.NET Razor Pages @page
¶
- ルートテンプレートは
URL
と同じように動作する - パスセパレーターで始まらないなら現在のページに対して相対的
- パスセパレーターで始まるなら絶対的
@page "{cityName}/{arrivalYear}-{arrivalMonth}-{arrivalDay}"
のような指定もありうる- 指定の注意
@page "{cityName}"
: 必ず何か値を指定しなければならず, 素のアクセスだと404
が出る@page "{cityName=paris}"
デフォルト値指定@page "{cityName?}"
: 無指定でもよい
ASP.NET Razor Pages @page
キャッチオールパラメータ¶
{*cityName}
: 生成されたURL内のパス区切り文字がURLエンコードされる/City/London/2022/4/18
は/City%2FLondon%2F2022%2F4%2F18
としてレンダリングされる
{**cityName}
: エンコーディングがデコード・ラウンドトリップされる- 、生成されたURLにリテラルパス区切り文字が含まれる
/City/London/2022/4/18
ASP.NET Razor Pages @page
ルート制約¶
"{cityName}/{arrivalYear:int}-{arrivalMonth:int}-{arrivalDay:int}"
{key:int}
の:int
の部分
"{cityName}/{arrivalDate:datetime}"
- 次のように有効な値の範囲指定もある
"{cityName}/{arrivalYear:int}-{arrivalMonth:range(1-12)}-{arrivalDay:int}"
- 制約の一覧
- いくつかの説明は次の通り
alpha
: 大文字または小文字のラテン アルファベット文字 (az または AZ) に一致bool
: ブール値に一致int
: 32ビット整数値に一致datetime
: DateTime値に一致decimal
: 10進数値に一致しますdouble
: 64ビットの浮動小数点値に一致float
: 32ビットの浮動小数点値に一致long
: 64ビット整数値に一致guid
: GUID値に一致length
: 指定された長さまたは指定された長さの範囲内の文字列に一致- cf.
{key:length(8)}
,{postcode:length(6,8)}
- cf.
min
: 最小値を持つ整数に一致- cf.
{age:min(18)}
- cf.
max
: 最大値を持つ整数に一致- cf.
{height:max(10)}
- cf.
minlength
: 最短の文字列に一致- cf.
{title:minlength(2)}
- cf.
maxlength
: 最長の文字列に一致- cf.
{postcode:maxlength(8)}
- cf.
range
: 値の範囲内の整数に一致- cf.
{month:range(1,12)}
- cf.
regex
: 正規表現に一致- cf.
{postcode:regex(^[A-Z]{2}\d\s?\d[A-Z]{2}$)}
- cf.
ASP.NET Razor Pages addTagHelper
ディレクティブ¶
- 2つの引数を取る
- 有効にするタグヘルパー
- 有効にするタグヘルパーを含むアセンブリ名
- ワイルドカード文字(
*
)は指定されたアセンブリ内のすべてのタグヘルパーを有効にする必要があることを指定 - フレームワークタグヘルパー:
Microsoft.AspNetCore.Mvc.TagHelpers
ASP.NET Razor Pages HTMLエンコードしたくない場合¶
- 基本は全てHTMLエンコードされる
@{
Layout = "_Layout";
const string output = "<p>This is a paragraph.</p>";
}
<div>
<p>@output</p>
<p>@Html.Raw("<p>This is a paragraph.</p>")</p>
</div>
ASP.NET Razor Pages HTMLエンコードレベルを下げる, WebEncoderOptions
¶
- キリル文字・中国語・アラビア語などの非ラテン語ベースの言語では, すべての文字が対応する
HTML
にエンコードされるため, 生成されるソースコードの文字数が大幅に増加する可能性がある. - 指定の参考
UnicodeRanges.All
を使うのも一手.
builder.Services.Configure<WebEncoderOptions>(options =>
{
options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Latin1Supplement);
});
ASP.NET Razor Pages removeTagHelper
ディレクティブ: 特定のタグの処理を選択的にオプトアウト¶
@removeTagHelper "Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers"
<!a href="https://www.learnrazorpages.com">Learn Razor Pages</!a>
@tagHelperPrefix x:
<x:a asp-page="/Index">Home</x:a>
ASP.NET Razor Pages _Layout
を探す場所¶
- 基本は
_ViewStart.cshtml
に指定するとよい: 全ての処理に先立って読まれる - 単に
Layout = “_Layout”;
とだけ書いて, 呼び出しページを\Pages\Admin\DestinationsOrders
に配置したときの検索場所は次の通り\Pages\Admin\DestinationsOrders\ _Layout.cshtml
\Pages\Admin\ _Layout.cshtml
\Pages\_Layout.cshtml
\Pages\Shared\ _Layout.cshtml
\Views\Shared\ _Layout.cshtml
ASP.NET Razor Pages URLの処理, スラッグなど¶
- 2022.ASP.NET_Core_Razor_Pages_in_Action.pdf, Chap 4
ASP.NET Razor Pages ViewBag
¶
ViewData
への別のアクセス方法だが非推奨ViewModel
中では使えないRazor
ファイルでだけ使える- プロパティはラッパーで, キーに一致するプロパティ名を介して項目にアクセスする
ASP.NET Razor Pages ViewData
¶
- 型づけも弱く, 利用は推奨されない
- ページタイトルなど小さく単純なデータをレイアウトページに渡す場合に便利
- 辞書ベースの機能
- 要素は
ViewDataDictionary
キーと値のペアとして, 大文字と小文字を区別しない文字列キーを参照してアクセスする
ViewData["Title"] = "Welcome!";
<title>@ViewData["Title"] - WebApplication1</title>
ASP.NET Razor Pages View Model¶
- ビューモデルとしての役割とコントローラーとしての役割がある
- ビューモデル: ビューに必要なデータだけのコンテナー
- コントローラー
ASP.NET Razor Pages View Model ハンドラーメソッドのパラメーター¶
?id=5
の形式で渡る:int
だと初期値は0
- ハンドラーメソッドはオーバーロードできない
- 受信データをパラメータに一致させる手法はモデルバインディング
@{
Layout = "_Layout";
}
<div>
<p>@Model.Message</p>
</div>
public class Index : PageModel
{
public string Message { get; set; } = string.Empty;
public void OnGet(int id)
{
Message = $"OnGet executed with id = {id}";
}
}
ASP.NET Razor Pages View Model 名前つきハンドラーメソッド¶
- フォームが二つあって区別して使いたい場合もある
asp-page-handler
とOnPostHoge
・OnPostFuga
などを使う
@page
@model WelcomeModel
@{
}
<div class="col">
<form method="post" asp-page-handler="Search">
<p>Search</p>
<input name="searchTerm" />
<button>Search</button>
</form>
<form method="post" asp-page-handler="Register">
<p>Register</p>
<input name="email" />
<button>Register</button>
</form>
<p>@Model.Message</p>
</div>
public class WelcomeModel : PageModel
{
public string Message { get; set; }
public void OnPostSearch(string searchTerm)
{
Message = $"You searched for {searchTerm}";
}
public void OnPostRegister(string email)
{
Message = $"You registered {email} for newsletters";
}
}
ASP.NET Razor Pages アクションの結果¶
PageResult
- ヘルパーメソッド:
Page
- 現在の
Razor
ページをレンダリングする
- ヘルパーメソッド:
FileContentResult
- ヘルパーメソッド:
File
- バイト配列、ストリーム、または仮想パスからファイルを返す
- ヘルパーメソッド:
NotFoundResult
- ヘルパーメソッド:
NotFound
- リソースが見つからなかったことを示すHTTP 404ステータスコードを返す
- ヘルパーメソッド:
PartialResult
Partial
- 部分的なビューまたはページをレンダリングする
RedirectToPageResult
RedirectToPage
,RedirectToPagePermanent
- ユーザーを指定されたページにリダイレクトする
RedirectToPage
: 一時的なリダイレクトの302
を表す.
StatusCodeResult
StatusCode
- 指定されたステータスコードでHTTP応答を返す
ASP.NET Razor Pages カスタム検証属性¶
- 2022.ASP.NET_Core_Razor_Pages_in_Action.pdf, 5.3.5
ASP.NET Razor Pages コメントの書き方¶
@*
と*@
で囲む
ASP.NET Razor Pages ビューコンポーネント¶
- ファイル・データベース・Webサービスなどの外部リソースへの呼び出しなど, 結果の
HTML
スニペットに含めるデータを取得または処理するために何らかのタイプのサーバー側ロジックが必要な場合, 部分ページではなくビュー コンポーネントが便利
ASP.NET Razor Pages 標準コードブロックとfunctions
コードブロックの違い¶
- 関数ブロック:
public
メンバーの宣言をサポート - 標準コードブロック: サポートされていない
ASP.NET Razor Pages 部分ビュー¶
@page
ディレクティブがないファイル- 規則ではないが, たいてい
_myPartial.cshtml
のようにファイル名の先頭にアンダースコアを付ける <partial name=”_NavigationPartial” />
で呼び出す
ASP.NET Razor Pages ページごとにテンプレートの一部だけ書き換える¶
RenderSectionAsync
メソッドをコンテンツを表示する場所に配置@await RenderSectionAsync(“ThingsToDoWidget”, false)
と第二引数をfalse
にしておくとよい
- 各ページで
@section
ディレクティブを使う - ページ固有の
JavaScript
ファイル読み込みにも使える - 参考メソッド
IsSectionDefined
IgnoreSection
ASP.NET Razor Pages ページモデル [DataType]
属性¶
- データバインディング・検証に使える
ASP.NET Razor Pages モデルバインディング¶
GET
メソッドに対しては[BindProperty(SupportsGet=true)]
まで指定する必要がある- HTMLタグの
name
にハイフンがあるe-mail
などは[BindProperty(Name="e-mail")]
のように書く必要がある - クラス自体に
[BindProperties]
をつけてもよい- オーバーポスティング攻撃があるため安易にクラスにつけてはならない
[BindProperty] public string CityName { get; set; } = string.Empty;
[BindProperty(SupportsGet=true)] public int Id { get; set; } = 0;
[BindProperty(Name="e-mail")] public string Email { get; set; } = "";
<div class="col-4">
<h3>モデルバインディング</h3>
<form method="post">
<div class="mb-3">
<label for="name">Enter city name</label>
<input class="form-control" type="text" name="cityName"/>
</div>
<button class="btn btn-primary">Submit</button>
</form>
@if (Request.HasFormContentType && !string.IsNullOrWhiteSpace(Model.CityName))
{
<p>You submitted @Model.CityName</p>
}
</div>
ASP.NET Razor Pages リテラル文字列の表示¶
@foreach (var city in cities)
{
if (city.Country == "UK")
{
@:Country: @city.Country, Name: @city.Name
}
}
@foreach (var city in cities)
{
if (city.Country == "UK")
{
<text>Country: @city.Country<br />
Name: @city.Name</text>
}
}
ASP.NET Razor Pages リモートバリデーション¶
ASP.NET Razor Pages ローカリゼーション¶
ASP.NET URL末尾に常にスラッシュをつける¶
AppendTrailingSlash
ASP.NET タグヘルパー¶
- サーバー側のプロパティとブラウザーにレンダリングされるフォームコントロールの間に双方向のバインディングが作れる
ASP.NET タグヘルパー asp-for
¶
<input asp-for="CityName" />
// => <input type="text" id="CityName" name="CityName" value="" />
ASP.NET タグヘルパー asp-format
¶
ASP.NET タグヘルパー [Display(Name="Hoge Fuga")]
¶
- 表示名が変更できる
[BindProperty]
public string Name { get; set; }
[BindProperty]
[Display(Name = "Maximum Number Of Guests")]
public int MaxNumberOfGuests { get; set; }
[BindProperty]
[Display(Name ="Day Rate")]
public decimal DayRate { get; set; }
[BindProperty]
[Display(Name = "Smoking Permitted")]
public bool SmokingPermitted { get; set; }
[BindProperty]
[DataType(DataType.Date)]
[Display(Name ="Available From")]
public DateTime AvailableFrom { get; set; }
ASP.NET タグヘルパー select
¶
- 書き方にいくつか選択肢があるため詳しくはドキュメントを見ること
- またはASP.NET Core Razor Pages in Action Chapter 6
- リストボックスを作る場合は
<select name="cities" multiple>
using Microsoft.AspNetCore.Mvc .Rendering
public class CreateModel : PageModel
{
[BindProperty]
[Display(Name = "City")]
public int SelectedCity { get; set; }
public SelectList Cities { get; set; }
public string Message { get; set; }
public void OnGet()
{
Cities = GetCityOptions();
}
public void OnPost()
{
Cities = GetCityOptions();
if (ModelState.IsValid)
{
var city = GetCityOptions().First(o => o.Value == SelectedCity.ToString());
Message = $"You selected {city.Text} with value of {SelectedCity}";
}
}
private SelectList GetCityOptions()
{
var cities = new List<City>
{
new City{ Id = 1, Name = "London"},
new City{ Id = 2, Name = "Paris" },
new City{ Id = 3, Name = "New York" },
new City{ Id = 4, Name = "Rome" },
new City{ Id = 5, Name = "Dublin" }
};
return new SelectList(cities, nameof(City.Id), nameof(City.Name));
}
private SelectList GetCityOptions2()
{
var cities = new List<SelectListItem>
{
new SelectListItem{ Value = "1", Text = "London"},
new SelectListItem{ Value = "2", Text = "Paris" },
new SelectListItem{ Value = "3", Text = "New York", Selected = true },
new SelectListItem{ Value = "4", Text = "Rome" },
new SelectListItem{ Value = "5", Text = "Dublin" }
};
return new SelectList(cities);
}
}
@if (Request.HasFormContentType)
{
<p>You selected @Model.SelectedCity</p>
<p>@Model.Message</p>
}
<form method="post">
<div class="mb-3">
<label class="form-label" asp-for="SelectedCity"></label>
<select class="form-control" asp-for="SelectedCity" asp-items="Model.Cities"></select>
</div>
<button class="btn btn-primary">Submit</button>
</form>
ASP.NET タグヘルパー select
, optGroup
¶
- 参考; ASP.NET Razor Pages in Action, 6.3.3
- リンク先は
Manning
のウェブ上の本のビューアー
- リンク先は
public string CountryName { get; set; }
private SelectList GetCityOptions()
{
var cities = new List<City>
{
new City{ Id = 1, Name = "Barcelona" , CountryName = "Spain" },
new City{ Id = 2, Name = "Cadiz" , CountryName = "Spain" },
new City{ Id = 3, Name = "London", CountryName = "United Kingdom" },
new City{ Id = 4, Name = "Madrid" , CountryName = "Spain" },
new City{ Id = 5, Name = "Rome", CountryName = "Italy" },
new City{ Id = 6, Name = "Venice", CountryName = "Italy" },
new City{ Id = 7, Name = "York" , CountryName = "United Kingdom" },
};
return new SelectList(cities, nameof(City.Id), nameof(City.Name), null, nameof(City.CountryName));
}
ASP.NET タグヘルパー ファイルアップロード¶
<form method="post" enctype="multipart/form-data">
の指定が重要
ASP.NET タグヘルパー ファイルアップロード時の拡張子制限¶
ASP.NET リクエストへの容量制限¶
[RequestSizeLimit(1048576)]
ASP.NET ルート制約の非同期処理¶
C# Delegate
¶
.NET
のデリゲートはメソッドシグネチャと戻り値の型を表す型.- 次の例では
MyDelegate
という名前のデリゲートを宣言DateTime
をパラメータとして整数を返す
delegate int MyDelegate(DateTime dt);
- 一致するシグネチャと戻り値の型に基づいてデリゲートにメソッドを割り当てる
int GetMonth(DateTime dt)
{
return dt.Month;
}
int PointlessAddition(DateTime dt)
{
return dt.Year + dt.Month + dt.Day;
}
MyDelegate example1 = GetMonth;
MyDelegate example2 = PointlessAddition;
Console.WriteLine(example1(DateTime.Now));
Console.WriteLine(example2(DateTime.Now));
- デリゲートにインライン匿名メソッドを割り当てる
MyDelegate example3 = delegate(DateTime dt) { return dt.Now.AddYears(-100).Year; };
Console.WriteLine(example3(DateTime.Now));
MyDelegate example4 = (dt) => { return dt.Now.AddYears(-100).Year; };
Console.WriteLine(example4(DateTime.Now));
dotnet-user-secrets¶
dotnet コマンドサンプル¶
dotnet new globaljson --sdk-version 6.0.400 --output <projname>
dotnet new mvc --no-https --output <projname> --framework net6.0
dotnet new sln -o <projname>
dotnet sln PartyInvites add <projname>
dotnet --list-sdks
dotnet watch
dotnet run
dotnet list package
dotnet add package <hoge> --version
dotnet remove package <hoge>
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef --version 6.0.0
dotnet ef --help
dotnet ef database drop --force --context StoreDbContext
dotnet tool uninstall -g Microsoft.Web.LibraryManager.Cli
dotnet tool install -g Microsoft.Web.LibraryManager.Cli --version 2.1.113
libman init -p cdnjs
libman install bootstrap@5.1.3 -d wwwroot/lib/bootstrap
dotnet コマンドラインでSDKのバージョンを指定する¶
- プロジェクト内では
global.json
でバージョンを指定する.
dotnet --list-sdks
dotnet ソリューション初期化¶
dotnet new sln # 現在のディレクトリに同名のファイル生成
dotnet new sln --name <MySolution> # 現在のディレクトリにファイル生成
dotnet new sln --output <MySolutionDirectory> # 指定したディレクトリに生成
dotnet ソリューションへのプロジェクト追加¶
dotnet sln add <HogeProject>
dotnet 使えるテンプレートのリスト¶
dotnet new --list
dotnet パッケージを削除する¶
dotnet remove package <hoge>
dotnet プロジェクト初期化時の参考¶
dotnet new globaljson --sdk-version <6.0.100> --output <MySolution/MyProject>
dotnet new web --no-https --output <MySolution/MyProject> --framework <net6.0>
dotnet new sln -o <MySolution>
dotnet sln <MySolution> add <MySolution/MyProject>
EF Core インストール¶
dotnet add package Microsoft.EntityFrameworkCore --version 6.0.12
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef --version 6.0.12
EF Core C#の型とデータベースの型を合わせる¶
[Column(TypeName = "decimal(8, 2)")]
などを使う.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SportsStore.Models;
public class Product
{
public long? ProductId { get; set; }
[Required(ErrorMessage = "Please enter a product name")]
public string Name { get; set; } = string.Empty;
[Required(ErrorMessage = "Please enter a description")]
public string Description { get; set; } = string.Empty;
[Required]
[Range(0.01, double.MaxValue,
ErrorMessage = "Please enter a positive price")]
[Column(TypeName = "decimal(8, 2)")]
public decimal Price { get; set; }
[Required(ErrorMessage = "Please specify a category")]
public string Category { get; set; } = string.Empty;
}
EF Core シードを使う¶
- 参考
Models/SeedData.cs
などを適切に作るProgram.cs
で読み込む
using SportsStore.Models;
SeedData.EnsurePopulated(app);
EF Core データベースのリセット¶
dotnet ef database drop --force --context <データベースコンテキスト>
EF Core 認証¶
ASP.NET Core Identity
を使う.
EF Core 認証用ユーザーにリレーションを張る¶
- 参考記事
- 参考実装: 2023/01
- 次のようなモデルを作る.
ApplicationUser : IdentityUser
がAspNetUsers
テーブルになる.
using Microsoft.AspNetCore.Identity;
namespace <ProjectName>.Models;
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTime BirthDate { get; set; }
public virtual IEnumerable<Article>? Articles { get; set; }
}
namespace <ProjectName>.Models;
public class Article
{
public int ArticleId { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string? UserId { get; set; }
public virtual ApplicationUser? User { get; set; }
}
EF Core 認証用ユーザーの主キーを変える¶
- URL
- 未検証
EF Core マイグレーションの作成¶
- ローカルインストールした
EF Core
を使う場合は次の通り.
dotnet dotnet-ef migrations add InitialCreate
dotnet dotnet-ef migrations add Rating
- コンテキストを指定したい場合は次の通り.
dotnet dotnet-ef migrations add InitialCreate -c <データベースコンテキスト>
EF Core
のツールをグローバルインストールしている場合は下記コマンド.
dotnet ef migrations add InitialCreate
EF Core マイグレーションの反映¶
dotnet dotnet-ef database update
EF Core マイグレーションのundo
¶
dotnet ef migrations remove
EF Core リレーションの設定¶
F# .env
ファイルを読み込みたい¶
- URL
.env
と書いたがini
ファイルなど同じように読める形式はもう少しあるはず.- シンプルは読み込み関数はない(?)らしい.
- 次のコードで環境変数に叩き込めるから後は環境変数読み込みの形で取ればよい.
open System
open System.IO
let parseLine(line : string) =
Console.WriteLine (sprintf "Parsing: %s" line)
match line.Split('=', StringSplitOptions.RemoveEmptyEntries) with
| args when args.Length = 2 -> Environment.SetEnvironmentVariable(args.[0], args.[1])
| _ -> ()
let load() =
lazy (
Console.WriteLine "Trying to load .env file..."
let currentDir = Directory.GetCurrentDirectory()
let pDir = Directory.GetParent(currentDir).ToString() // 都合によって親ディレクトリの.envを読んでいるが必要なければ削除
let filePath = Path.Combine(pDir, ".env") // 親ディレクトリの.envを読んでいるので同じディレクトリにあるなら適切に書き換える
filePath
|> File.Exists
|> function
| false -> Console.WriteLine "No .env file found."
| true -> filePath |> File.ReadAllLines |> Seq.iter parseLine
)
let init = load().Value
// 環境変数から読み込む部分
let apiRootUrl = Environment.GetEnvironmentVariable "NEXT_PUBLIC_API_ROOT_URL"
F# bottom¶
let undefined<'T> : 'T = failwith "Not implemented yet"
let stub1 (x : int) : float = undefined
let stub2 (x : 'T) : 'T = undefined
let undefined<'T> : 'T = raise (NotImplementedException())
F# fsx実行: 競プロ用実行¶
dotnet fsi hoge.fsx
dotnet fsi hoge.fsx < input.txt
F# fsxで他のfsxを読み込む¶
#load "Script1.fsx"
open Script1
F# Fable¶
メモ¶
dotnet new --install Fable.Template
dotnet new fable
F# Json¶
- 参考
C#
のコードを参考にすればよいmutable
と<-
を使うのが大事
open System.Text.Json
open System.Text.Json.Serialization
open System.Text.Encodings.Web
open System.Text.Unicode
let mutable options = JsonSerializerOptions()
options.Encoder <- JavaScriptEncoder.Create(UnicodeRanges.All)
let jsonString = JsonSerializer.Serialize(items, options)
System.IO.File.WriteAllText(outputJsonFileName, jsonString, System.Text.Encoding.UTF8)
F# Stack overflow¶
AtCoderの木DPで, Pythonコードに沿って実装してみたらStack overflowしたのでその対処の記録.
- 再帰関数で stack overflow しないように末尾再帰に書き直すのはF#だけでなく関数プログラミングで必須技能なので練習しよう
- 再帰の回数の上限ではなくスタックに割り当てられるメモリの量に制限がある
- 管理は.NETではなくOS
- Linuxなら
ulimit -s
で変更できるはず - 参考: Python版
- resource.setrlimit()はUnixのシステムコールを呼んでいるだけ
F# Stringにはreverseがない(?)ので代替策を見つけた¶
どなたか有識者の方, いい感じのメソッドがあれば教えてほしい. 今回はProject Euler Problem 4の回文数の問題を解いているときに出くわした. どうもStringにはreverseメソッドがないらしい. 検索した結果, 今回はこのページのコピペとして次のコードを採用した.
let reverse (s : string) = s |> Seq.toArray |> Array.rev |> System.String
s |> Seq.rev |> System.String.Concat
あと最近F#のリファレンスがGitHubにうつったようだ. そのリンクも改めて貼っておこう.
GitHubのAlgorithmsAndDataStructureByFSharpにアルゴリズム系のコードと共にF#の「ライブラリ」という名の自分用コードサンプル集を作ってある. 具体的にはLibraryディレクトリがそう. そのうち競プロ用にいろいろ調べたことを改めてLibraryにまとめ直したい. 何はともあれ,
F#, とにかく情報がないので地道に貯めていく.
F# 型プロバイダー FSharp.Data
の型プロバイダーには定数しか渡せない¶
- URL
- 下記コードで
FSharp.Data.JsonProvider<"http://localhost">
の<>
内は変数にできない
#r "nuget: FSharp.Data"
open FSharp.Data
open FSharp.Data.HttpRequestHeaders
let toJson = fun (x: HttpResponse) ->
match x.Body with
| Text t -> t
| _ -> failwith "bytes"
/// http://localhost
type Root = FSharp.Data.JsonProvider<"http://localhost">
Http.Request("http://localhost", httpMethod = "GET") |> toJson |> fun t -> Root.Parse(t)
F# 型プロバイダー 実行時のディレクトリ指定¶
- URL
ResolutionFolder=__SOURCE_DIRECTORY__
を指定する
#r "nuget: FSharp.Data"
open FSharp.Data
type Comments = JsonProvider<"sampleResponse.json", ResolutionFolder=__SOURCE_DIRECTORY__>;;
F# 関数に型をつける¶
ふつうの方法¶
let f (i:int) (j:int): int = i * j
ラムダを使う方法¶
let f: int -> int -> int =
fun i j -> i * j
F# 空シーケンスの判定, 特にmatch
¶
- 空シーケンスのリテラルがないので
s when Seq.isEmpty s
のように書く.
let rec dropWhile p xs =
match xs with
| s when Seq.isEmpty s -> Seq.empty
| _ -> if p (Seq.head) then dropWhile p (Seq.tail xs) else (Seq.tail xs)
F# 遅延リストはとりあえずSeq
¶
- 残念ながらHaskellのリストなどと同じ書き味にはならない
Seq.initInfinite (fun i -> 2 * i + 1)
F# ファイルの読み書き¶
C# (.NET)
のメソッドを使おう- JSONの場合の注意
- 既定のエンコーダーと比較して
UnsafeRelaxedJsonEscaping
エンコーダーは文字をエスケープせずにそのまま渡すことについてより寛容 <
,>
,&
,'
などHTMLに影響する文字はエスケープされないs.Replace(@"\u002B", "+").Replace(@"\u003C", "<").Replace(@"\u0027", "'").Replace(@"\u0026", "&").Replace(@"\u003E", ">")
で置換する
- 既定のエンコーダーと比較して
let fileName = "1.tmp.txt"
System.IO.File.WriteAllText(fileName, someString, System.Text.Encoding.Default)
F# 複数行の文字列, ヒアドキュメント¶
C#と同じように書ける模様. 特に三重引用符または「@+クオート」で書ける. ついでに逐次的リテラル文字列の概念があり, 後者の「@+クォート」形式がそれ. 例を引用しておく.
".¥¥hoge¥¥hoge¥¥hoge.txt" // ふつうの文字列
@".¥hoge¥hoge¥hoge.txt" // 逐次的リテラル文字列