Nuget

ClosedXMLをインストール

ファイルを開いてシート名をすべて表示

using ClosedXML.Excel;

try
{
    var filePath = @"c:\temp\Sample.xlsx";
    using (var workbook = new XLWorkbook(filePath))
    {
        Console.WriteLine($"シート数: {workbook.Worksheets.Count}");

        foreach (var sheet in workbook.Worksheets)
        {
            Console.WriteLine($"シート名: {sheet.Name}");
        }
    }
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

Excelを開いていると、ファイルオープン時にエラーになるので・・・

読み取り専用で開く場合は、ClosedXML自体には機能がないので

FileStreamで読み取り専用で開いてそれを使って開くとよい

using ClosedXML.Excel;

try
{
    var filePath = @"c:\temp\Sample.xlsx";

    // 読み取りで開き、他ファイルは読み書きOKとする
    using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        using (var workbook = new XLWorkbook(fileStream))
        {
            Console.WriteLine($"シート数: {workbook.Worksheets.Count}");

            foreach (var sheet in workbook.Worksheets)
            {
                Console.WriteLine($"シート名: {sheet.Name}");
            }
        }
    }

}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

Excelで開いている間にClosedXMLで開くとエラーになるが、
CLosedXMLで開いている間にExcelは開くことが可能なので、処理して保存する際にエラーとなる可能性はある。
そういうケースを気にするのであれば、FileStreamで排他制御する必要あり。

using ClosedXML.Excel;

try
{
    var filePath = @"c:\temp\Sample.xlsx";

    // 読み書き開き、他ファイルは読み書き禁止とする
    using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
    {
        using (var workbook = new XLWorkbook(fileStream))
        {
            Console.WriteLine($"シート数: {workbook.Worksheets.Count}");

            foreach (var sheet in workbook.Worksheets)
            {
                Console.WriteLine($"シート名: {sheet.Name}");
            }
        }
    }

}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

セルの操作

A1形式でアクセス

worksheet.Cell(2, "C").value

行列でアクセス

worksheet.Cell(2, 3).value
投稿日時: 2025-04-24 13:23:24
更新日時: 2025-04-24 13:43:24
C#

C#で利用可能な形態素解析は、Mecabになります。

NMeCabか MeCab.DotNetの2択になります

経緯については、以下の2つを見るとよくわかります

リポジトリ移転について 2010年からOSDNで開発し公開してきたNMeCabですが、バージョン0.10.0からは、こちらGitHubで開発し公開していきます。 NMeCab MeCab.DotNet

これは何? NOTE: 将来的に、MeCab.DotNetとNMeCabを統合する作業をしています。 詳しくはこのissueを参照して下さい。 "MeCab" は、日本語形態素解析エンジンのプロジェクトです。 "NMeCab" は、上記MeCabを、.NET Framework 2.0のマネージライブラリとして実装し直したものです。ただ、もう更新されていないようです... --> GitHubで復活しました 2010年からOSDNで開発し公開してきたNMeCabですが、バージョン0.10.0からは、こちらGitHubで開発し公開していきます。 MeCab.DotNet

NMeCabは、もともとOSDNで開発されていて、.NET Frameworkが要件となっていた
その後、 別の人が .NETでも使えるように作ったのが MeCab.DotNetになります
その後、 NMeCabが OSDNから GitHubに移行し、.NETの対応を行ったため、

結論としては、 NMeCabを使えばよいという話になります

Nugetに至っては少しややこしい状況になっています。
単純にNMeCabを検索すると、作者とは別の方の名前でヒットします。
これは、説明にもありますが"An unofficial package of NMecab. "
作成者の作ったものではなく別の方が作ったものになります
ずいぶん古いものがそのまま残っているのでこちらは見なかったことにしましょう・・・

では、どれをつかえばよいかという話ですが、GitHubのページの説明では
LibNMeCabが該当するライブラリとのこと
辞書パッケージを選択してインストールすると依存関係からLibNMeCabも入るよとのこと

辞書リソースは、Disposeして解放すればよいとのこと。

Taggerインスタンスが確保している辞書リソースへのハンドルを解放するのが、この Dispose() メソッドです。(.NETプログラミングに慣れない方は注意して下さい) もちろん、一度確保した辞書リソースを再利用したい場合には、usingステートメントを記述せず、Taggerインスタンスをスコープの広い変数に保持して使い回すこともできます。アプリケーション終了時など任意のタイミングでDisposeしてください。

using NMeCab.Specialized;
using System;

class Program
{
    static void Main(string[] args)
    {
        var sentence = "「これはサンプルテキストです。」 This is a sample text.\n";

        using(var tagger = MeCabIpaDicTagger.Create())
        {
            foreach (var node in tagger.Parse(sentence))
            {
                Console.WriteLine($"文字列:{node.Surface}\t読み:{node.Reading}\t品詞:{node.PartsOfSpeech}");
            }
        }
    }
}

出力:

文字列:「      読み:「        品詞:記号
文字列:これ    読み:コレ      品詞:名詞
文字列:は      読み:ハ        品詞:助詞
文字列:サンプル        読み:サンプル  品詞:名詞
文字列:テキスト        読み:テキスト  品詞:名詞
文字列:です    読み:デス      品詞:助動詞
文字列:。      読み:。        品詞:記号
文字列:」      読み:」        品詞:記号
文字列:       読み:         品詞:記号
文字列:This    読み:  品詞:名詞
文字列:is      読み:  品詞:名詞
文字列:a       読み:  品詞:名詞
文字列:sample  読み:  品詞:名詞
文字列:text    読み:  品詞:名詞
文字列:.       読み:  品詞:名詞
投稿日時: 2024-12-30 12:08:30
更新日時: 2024-12-30 16:11:30

マイグレーションの時に、列の文字数長さの設定でmaxの部分でエラーになるので、それぞれにどんな情報が入っていて、何を設定すべきか調べてみた。

Identityで生成されるテーブルとそれらの関係は以下のとおり

7つのテーブルがあり、Userとそれに関連するテーブルが4つと
Roleとそれに関するテーブルが2つと
UserとRoleを結ぶテーブルが1つの計7つで構成されている

AspNetUsersテーブル

AspNetUserテーブルは、ユーザーに関する主要な情報が格納

列名 nvarchar(max) 内容
Id ユーザーの一意の識別子。通常はGUID(ハイフンあり)
UserName ユーザーのログイン名
NormalizedUserName 検索などの比較で効率的に行うための正規化(大文字)
Email メールアドレス
NormalizedEmail 検索などの比較で効率的に行うための正規化(大文字)
EmailConfirmed メールアドレスによる確認ができたかを示すブール値
PasswordHash ユーザーパスワード(Hash値)
SecurityStamp セキュリティ目的で使用される一意の値
ConcurrencyStamp 同時実行制御に使用。GUIDが使われる
PhoneNumber 電話番号
PhoneNumberConfirmed 電話番号による確認ができたかを示すブール値
TwoFactorEnabled 二要素認証が有効かどうかを示すブール値
LockoutEnd アカウントロックアウトが終了する日時
LockoutEnabled アカウントのロックアウトが有効かどうかのブール値
AccessFailedCount ログイン失敗回数

・AIの見解
PasswordHash・・・一般的なハッシュアルゴリズム(例:SHA256)の出力は64文字程度です。将来的なハッシュアルゴリズムの変更に対応できる余裕を持たせて128文字程度が妥当ではないか

SecurityStamp・・・セキュリティスタンプはGUIDベースで生成されることが多いです。GUIDの文字列表現は通常36文字です。よって36文字

ConcurrencyStamp・・・同時実行制御に使用されるタイムスタンプです。通常GUIDが使用される。GUIDの文字列表現は通常36文字。よって36文字

PhoneNumber・・・国際電話番号を考慮すると15桁程度。中継電話会社経由の番号だと16桁になることもある。電話番号の桁数はいつかわってもおかしくないので多少余裕をもたせるとよさそう。よって20文字

AspNetUserClaimsテーブル

AspNetUserClaimsテーブルは、ーザーに関連付けられたクレーム情報が格納

列名 nvarchar(max) 内容
Id プライマリキーとして機能する一意の識別子
UserId クレームを設定するUserId
ClaimType クレームの種類や名前
ClaimValue クレームの値

クレームは、ユーザーはロールといった概念のほかに情報を追加し細かい認証の制御を行うために使用する
よって、クレームの種類や値を設定するのは開発者次第のためお好きな文字数を設定すればよい
長い名称を使うことなければ64文字とかで十分かなと

AspNetUserLoginsテーブル

AspNetUserLoginsテーブルは、外部認証プロバイダーを使用してログインしたユーザーの情報が格納

列名 nvarchar(max) 内容
LoginProvider 外部認証プロバイダーの名前(例:Google、Facebook、Microsoft等)
ProviderKey 認証プロバイダーがユーザーに割り当てた一意の識別子
ProviderDisplayName 認証プロバイダーの表示名を格納
UserId 該当するUserId

ProviderDisplayNameは、例えばGoogleの認証だとGoogleが格納されています。そこまで長い名前は入るとおもえないので、64文字程度もあれば十分かなと

AspNetUserTokensテーブル

AspNetUserTokensテーブルは、ユーザーに関連する各種トークン情報が格納

列名 nvarchar(max) 内容
UserId AspNetUsersテーブルのIdに対応する、ユーザーの一意の識別子
LoginProvider トークンを生成したプロバイダーの名前を指定
Name トークンの名前や種類を格納
Value トークンの実際の値を格納

どういうときにつかうのか?
パスワードリセット、メール確認、二要素認証、外部認証プロバイダー、リフレッシュトークン、カスタムトークンなどだそうです

AIの見解: OAuth 2.0のアクセストークンは通常200〜400文字程度
JWTベースのトークンは500〜1000文字程度になることがあります
リフレッシュトークンは通常、アクセストークンよりも長くなる傾向があります
トークンのサイズが将来的に大きくなる可能性を考慮する必要があります
よって2000文字程度の長さが妥当では?
とのこと。

AspNetUserRolesテーブル

AspNetUserRolesテーブルは、ユーザーとロールの関連付けに関する情報が格納

列名 nvarchar(max) 内容
UserId UserIdを設定
RoleId RoleIdを設定

AspNetRolesテーブル

AspNetRolesテーブルは、アプリケーション内のロールに関する情報が格納

列名 nvarchar(max) 内容
Id UserIdを設定
Name RoleIdを設定
NormalizedName 検索などの比較で効率的に行うための正規化(大文字)
ConcurrencyStamp 同時実行制御に使用。GUIDが使われる

・AIの見解
ConcurrencyStamp・・・同時実行制御に使用されるタイムスタンプです。通常GUIDが使用される。GUIDの文字列表現は通常36文字。よって36文字

AspNetRoleClaimsテーブル

AspNetRoleClaimsテーブルは、ロールに関連付けられた追加の情報(クレーム)が格納
AspNetUserClaimsのRole版

列名 nvarchar(max) 内容
Id プライマリキーとして機能する一意の識別子
RoleId クレームを設定するRoleId
ClaimType クレームの種類や名前
ClaimValue クレームの値

クレームは、ユーザーはロールといった概念のほかに情報を追加し細かい認証の制御を行うために使用する
よって、クレームの種類や値を設定するのは開発者次第のためお好きな文字数を設定すればよい
長い名称を使うことなければ64文字とかで十分かなと


同時実行制御について

同じレコードを複数のリクエストにより変更を行った際に、どちらのリクエストで変更したのか、判断するために、ConcurrencyStampにGUIDを設定し判断に使っています。

  1. Selectで対象のレコードの情報を取得する
  2. 新しいGUIDを取得する
  3. 対象レコードを取得する際に、1で取得したConcurrencyStampも条件に含めレコードをしぼり更新を行う。その際に新しいGUIDを設定する

これにより、同時実行した場合、更新ができれば自分のリクエストにより更新ができたことになり
更新ができない場合(他のリクエストによりすでにConcurrencyStampの値が変わっていた場合)は更新が失敗することになります

投稿日時: 2024-10-12 06:24:12
更新日時: 2024-10-12 12:13:12

該当のクラスデフォルトの説明あり

各説明とデフォルト値ですね

builder.Services.Configure<IdentityOptions>(options =>
{
    // ロックアウトの設定
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); // ロックアウトの期間
    options.Lockout.MaxFailedAccessAttempts = 5; // ロックアウトになる前の最大失敗試行回数
    options.Lockout.AllowedForNewUsers = true; // 新しいユーザーに対してロックアウトを許可するかどうか
});

AllowedForNewUsersは、アカウント作ったあとの初回ログインでロックアウトを許容するかどうかという設定とのこと
作ったばかりなので間違える可能性を考慮してということだろうか。。


ただ、上記設定を利用するかどうかは、Loginする際のメソッドに渡す引数できまる
具体的には、以下のようにコントローラーのLoginのメソッドのところで、 PasswordSignInAsyncメソッドに渡すlockoutFailureがtrueだと
MaxFailedAccessAttemptsの回数に応じてロックアウトされる

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using SampleWeb.Models;

namespace SampleWeb.Controllers
{
    public class AccountController : Controller
    {
        private readonly SignInManager<IdentityUser> _signInManager;

        public AccountController(SignInManager<IdentityUser> signInManager)
        {
            _signInManager = signInManager;
        }

        public IActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Login(LoginModel model)
        {
            if (ModelState.IsValid)
            {
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
                if (result.Succeeded)
                {
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                }
            }
            return View(model);
        }
    }
}
投稿日時: 2024-08-31 19:01:31
更新日時: 2024-09-01 10:33:01

一次ソースがみつからなかったので生成AIの情報をそのままペタッと
./program.csに以下項目でidentityに関するcookieの挙動を設定ができるそうです

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromDays(14);

    options.LoginPath = "/Account/Login";
    options.AccessDeniedPath = "/Account/AccessDenied";
    options.SlidingExpiration = false;
});

HttpOnly・・・クッキーが HTTP リクエストでのみ送信され、JavaScript からはアクセスできないようにします
補足:セキュリティ: HttpOnly フラグが設定されたクッキーは、クロスサイトスクリプティング(XSS)攻撃から保護されるため、セキュリティが向上します。JavaScript からクッキーの値を読み取ることができなくなり、クッキーに保存されている情報(例えば認証トークン)が盗まれるリスクが低くなります

ExpireTimeSpan・・・クッキーの有効期限を設定します。ここでは、クッキーが 14日後に期限切れとなります

LoginPath・・・ 認証が必要なリソースにアクセスしようとしたときに、リダイレクトされるログインページのパスを指定します

AccessDeniedPath・・・ユーザーがアクセス権のないリソースにアクセスしようとしたときにリダイレクトされるページのパスを指定します

SlidingExpiration ・・・クッキーの有効期限が、ユーザーのアクティビティがあるたびに延長されるかどうかを指定します


HttpOnlyはセキュリティの観点からtrue
SlidingExpiration もセキュリティの観点からfalse(時間がきたら強制的に切れる状態のほうが安全)
パス関係は、必要に応じてカスタムページを設定した場合は、その設定を行う必要アリだと思います
ExpireTimeSpanは、期間が短いとちょくちょくログインしなおさないといけないのでほどほどのタイミングに設定しておく必要があると思います

投稿日時: 2024-08-31 15:38:31
更新日時: 2024-09-01 05:33:01

ユーザー名の制約はこちら。

ユーザー名は、AllowedUserNameCharctersに設定されている文字しかつかえないです。つまり日本語はNGです。 RequireUniqueEmailは、ユーザー名とEmailの関係の設定になります false…ユーザー名は一意となる。同一メールアドレスでも異なるユーザー名が設定可能 true... デフォルトはfalseです

検証してみます
以下プログラムで、同一メールアドレスで複数のユーザー名が入るかテストしてみます

参考_同一メールアドレスで異なるユーザー名
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace SampleWeb.Data
{
    public class SeedData
    {
        public static async Task Initialize(IServiceProvider serviceProvider)
        {
            var userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();

            var email1 = "username@example.com";
            var user1 = new IdentityUser()
            {
                UserName = "username1",
                Email = email1,
                EmailConfirmed = true
            };
            await userManager.CreateAsync(user1, "Password@12345");

            var email2 = "username@example.com";
            var user2 = new IdentityUser()
            {
                UserName = "username2",
                Email = email2,
                EmailConfirmed = true
            };
            await userManager.CreateAsync(user2, "Password@12345");
        }
    }
}

雑なプログラムですが・・・→2つとも入ります


ユーザー名は一意として扱われ、異なるEmailであってもNGになります

参考_同一ユーザー名異なるemailで登録
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace SampleWeb.Data
{
    public class SeedData
    {
        public static async Task Initialize(IServiceProvider serviceProvider)
        {
            var userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();

            var email1 = "username1@example.com";
            var user1 = new IdentityUser()
            {
                UserName = "username",
                Email = email1,
                EmailConfirmed = true
            };
            await userManager.CreateAsync(user1, "Password@12345");

            var email2 = "username2@example.com";
            var user2 = new IdentityUser()
            {
                UserName = "username",
                Email = email2,
                EmailConfirmed = true
            };
            await userManager.CreateAsync(user2, "Password@12345");
        }
    }
}

これだと最初の設定しか入らない

投稿日時: 2024-08-31 13:05:31
更新日時: 2024-08-31 16:40:31
デフォルトの条件は、6文字以上 数字、大文字、小文字、英数字以外をそれぞれ最低1文字含むことが条件

この条件を変えたい場合は、

./program.cs

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SampleWeb03.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddControllersWithViews();

// パスワードの条件設定
builder.Services.Configure<IdentityOptions>(options =>
{
    options.Password.RequiredLength = 10;           // 長さ

    options.Password.RequireUppercase = true;       // 大文字
    options.Password.RequireLowercase = true;       // 小文字
    options.Password.RequireDigit = true;           // 数字
    options.Password.RequireNonAlphanumeric = true; // 英数以外の文字
    options.Password.RequiredUniqueChars = 1;       // 必須項目がそれぞれ最低いくつ必要か?
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/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.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();

パスワードの条件設定のように、それぞれを設定する
必須な種類はすべてtrueがデフォルトなのであえて設定するまでもなく
変更するとしたら文字列の長さくらいかなと

投稿日時: 2024-08-31 11:06:31
更新日時: 2024-08-31 13:22:31

このページでは、下記ページの状態から開始しています
ASP.NET Core Webアプリで、SQLiteを使用して認証用のDBを作成


認証を使用したプロジェクトを作成し、そのまま起動するとデフォルトのUIが利用できます。

・認証は個別のアカウント


・デフォルトのまま起動すると、登録やログインのリンクあり。


・登録のページ


・ログインのページ


個人で利用するブログを作ったとすると、自分が利用するアカウントを登録してしまえば、登録画面は不要でログイン画面だけほしいという話になります。
そのため、起動時にSeedとして初期のアカウントを登録させて、不要なページは program.csファイル内で強制的にリダイレクトかけたりエラーにすることも可能ですが・・・
対応漏れがあると目もあてられないことになりかねないので、デフォルトのUIを無効とし、必要なページだけ設定していきます。

デフォルトのUI

./Program.cs ファイル中でAddDefaultIdentityを利用していることで有効になります。(フレームワークのバージョンにより記載はことなるので要注意)

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

### デフォルトのUIを無効にする

比較しやすいよう元のコードも残してあります。

./program.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SampleWeb.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

//builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
//    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => { options.SignIn.RequireConfirmedAccount = true; })
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/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.UseAuthentication(); //add
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

//app.MapRazorPages(); //del

app.Run();

AddDefaultIdentityからAddIdentityに変更しています。
app.MapRazorPages()は、自動でデフォルトページへのマップをするもので、デフォルトページを使わないため不要になります。
また、カスタムページからの認証をおこなうために、app.UseAuthentication()を追加しています。

./Views/Shared/_LoginPartial.cshtml

    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>

asp-areaとasp-pageから /Identity/Account/Register や /Identity/Account/Loginというページを生成していましたがこれらの設定に切り替えると設定されなくなります。

before: https://localhost:{ポート番号}/Identity/Account/Login after: https://localhost:{ポート番号}/?area=Identity&page=%2FAccount%2FLogin
### アカウントの追加

ここでは、起動時にアカウントが存在しなければ追加するようにします。

SeedDataクラスを作成し、追加する処理を記載します。

./Data/SeedData.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace SampleWeb.Data
{
    public class SeedData
    {
        public static async Task Initialize(IServiceProvider serviceProvider)
        {
            var userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();

            var email = "username@example.com";
            var user = await userManager.FindByEmailAsync(email);

            if (user == null)
            {
                user = new IdentityUser()
                {
                    UserName = email,
                    Email = email,
                    EmailConfirmed = true
                };
                await userManager.CreateAsync(user, "Password@12345");
            }
        }
    }
}

メールは、 username@example.com パスワードは Password@12345にしてみました。
制約をみたすものでないとDBに追加されません。

  • ユーザー名はデフォルトだと日本語はつかえません
  • パスワードは、6文字以上数字、大小アルファベット、英数字以外を含む必要があります。

※いうまでもないですが、実際に使用するパスワードを記載してgithubにあげるようなことだけは・・・


次は、SeedDataクラスの呼び出しです。

./program.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SampleWeb.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

//builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
//    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => { options.SignIn.RequireConfirmedAccount = true; })
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddControllersWithViews();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/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.UseAuthentication(); //add
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

// 初期アカウントの追加
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    await SeedData.Initialize(services);
}
//app.MapRazorPages(); //del

app.Run();
最後のほうで、初期アカウントの追加として記載してあります。 これで、ルールにのっとった、email、username、passwordを指定してあればAspNetUsersテーブルに追加されます。 ※SQLiteのクライアントソフトは、DB Browser for SQLiteを使用

ログイン画面

ログイン画面作っていきます
ログインやログアウトの処理はフレームワークに含まれているので、必要なモデルを作成しお作法にのっとって記述していきます。


まずはモデル
./Models/LoginModel.cs

using System.ComponentModel.DataAnnotations;

namespace SampleWeb.Models
{
    public class LoginModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; } = string.Empty;

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; } = string.Empty;

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; } = false;
    }
}

コントローラー
./Controllers/AccountController.cs

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using SampleWeb.Models;

namespace SampleWeb.Controllers
{
    public class AccountController : Controller
    {
        private readonly SignInManager<IdentityUser> _signInManager;

        public AccountController(SignInManager<IdentityUser> signInManager)
        {
            _signInManager = signInManager;
        }

        public IActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Login(LoginModel model)
        {
            if (ModelState.IsValid)
            {
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
                if (result.Succeeded)
                {
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                }
            }
            return View(model);
        }
    }
}

Accountコントローラーを追加
ログイン、ログアウトはSignInManagerを扱うのでコンストラクタで追加して保持。
Loginのページ表示(GET)とログイン処理を行う(POST)を作成


ビュー
./Views/Account/Login.cshtml

@model SampleWeb.Models.LoginModel

@{
    ViewData["Title"] = "ログイン画面";
}

<div class="row">
    <div class="col-6">
        <form id="account" asp-controller="Account" asp-action="Login" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating mb-3">
                <input asp-for="Email" class="form-control" autocomplete="username" aria-required="true" />
                <label asp-for="Email" class="form-label">Email</label>
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>

            <div class="form-floating mb-3">
                <input asp-for="Password" class="form-control" autocomplete="current-password" aria-required="true" />
                <label asp-for="Password" class="form-label"></label>
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>

            <div>
                <div class="checkbox">
                    <label asp-for="RememberMe" class="form-label">
                        <input class="form-check-input" asp-for="RememberMe" />
                        @Html.DisplayNameFor(m => m.RememberMe)
                    </label>
                </div>
            </div>

            <div>
                <button id="login-submit" type="submit" class="btn btn-primary">Log in</button>
            </div>
        </form>
    </div>
</div>

HTMLはテンプレートとして組み込まれているもののうち、ログインフォーム部分だけとってきています
もともとRazorPageだったので手直ししてあります


呼び出しもとのリンクのURLを直していきます。
./Views/Shared/_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity?.Name!</a>
    </li>
    <li class="nav-item">
        <form  class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
@*     <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li> *@

        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Login</a>
        </li>

}
</ul>

こちらも比較しやすいように元のソース残してあります。
asp-area、asp-pageを削除して、controllerとactionを指定しただけです
確認のため起動します


Registerは消えて、Loginのみ。デフォルトのUIは無効にしているので直接URLを入力してもたどりつけません。

ログインのURLは、/Account/Loginになっています。

見た目はほぼいじっていないので、質素な感じですが、最低限のものは表示できています

EmailとPasswordを入力して送信するとログインできます


ログアウト

ログアウトの処理をControllerに追加します
./Controllers/AccountController.cs

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using SampleWeb.Models;

namespace SampleWeb.Controllers
{
    public class AccountController : Controller
    {
        private readonly SignInManager<IdentityUser> _signInManager;

        public AccountController(SignInManager<IdentityUser> signInManager)
        {
            _signInManager = signInManager;
        }

        public IActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Login(LoginModel model)
        {
            if (ModelState.IsValid)
            {
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
                if (result.Succeeded)
                {
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                }
            }
            return View(model);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return RedirectToAction("Index", "Home");
        }
    }
}

Logout(Post)を追加しました

ログアウトのリンクを修正していきます
./Views/Shared/_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity?.Name!</a>
    </li>
    <li class="nav-item">
            @*         <form  class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })">
        <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form> *@

            <form class="form-inline" asp-controller="Account" asp-action="Logout" method="post">
                <button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
            </form>
    </li>
}
else
{
@*     <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li> *@

        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Login</a>
        </li>

}
</ul>

Login同様に、 asp-area、asp-pageを削除して、controllerとactionを追加
postメソッドに飛ばしたいので、postを追加
起動して、ログイン→ログアウトができています


おまけ

個人利用のwebアプリとはいえ、パスワードはソースに書きたくない・・・
という場合は、パスワードを入れる部分はデフォルトのUIを一時的に有効にして追加し入れ終わったら無効にするでもよいかもしれないですね。 ./program.csは以下の状態にします。

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SampleWeb.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

//builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => { options.SignIn.RequireConfirmedAccount = true; })
//    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/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.UseAuthentication(); //add
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");


app.MapRazorPages(); //パスワード登録したらコメントアウト

app.Run();

プログラム実行し、直接登録のURLを実行します
https://localhost:{ポート番号}/Identity/Account/Register


必要な項目を追加して、Registerを実行


以下ページが表示されます

これは、program.csで登録時の認証がデフォルトはtrueになっているものの、メール送信の方法がないから表示されています
./program.cs

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

※RequireConfirmedAccount = true の部分。falseならメールによる確認を行わない設定

最初アカウントを登録すると AspNetUsersテーブルのEmailConfirmedが0になっています。この状態だとログインできません

登録とどうじにこのメールアドレスにメールを飛ばすことになるのですが、その処理が追加されていないため、このような画面を表示しています

実運用だとメールを飛ばして、そこからリンククリックしてもらい認証完了というところですがそうなっていないためアカウントを有効にすることができないです
ただし、英文をよくよむと

This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. Normally this would be emailed: Click here to confirm your account

メール送信の設定内からこのドキュメントみてねって話と
メールないなら有効にする手段ないから、ここクリックしてねと書いてあります
よって、今回の目的では、リンクをクリックして認証の代わりをするか、設定をfalseにするかのどちらかの対応が必要です
(直接テーブルの値を1に変えても一緒ですが・・・)


リンクをクリックすると以下画面に遷移し、AspNetUsersテーブルのEmailConfirmedの値が1に代わっていることが確認できます


ここまでできたら、デフォルトのUIをコメントアウトで無効にする でもよいですね。

投稿日時: 2024-08-31 02:41:31
更新日時: 2024-08-31 16:40:31

プロジェクトの作成

ASP.NET Core Webアプリ(Model-View-Controller)を選択

以下で作成


パッケージの更新及び追加

ソリューションのNugetパッケージの管理を開く
更新プログラムがあるので、更新を行う

Microsoft.EntityFrameworkCore.Sqliteをインストールする


認証関係のDBを作成

以下ファイルを変更する
./appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=Identity.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

SQLiteの接続文字列を設定。

./Program.cs

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));

UseSqlServerをUseSqliteに変更する。

パッケージマネージャーコンソールを起動

Update-Database -Context ApplicationDbContext

Update-Databaseコマンドを実行しDBを作成する。 ※複数のプロジェクトがある場合、パッケージマネージャーコンソールの規定のプロジェクトは、対象のプロジェクトを選択しておくこと。

エラー発生
PM> Update-Database -Context ApplicationDbContext
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "MigrationId", "ProductVersion"
      FROM "__EFMigrationsHistory"
      ORDER BY "MigrationId";
Microsoft.EntityFrameworkCore.Migrations[20402]
      Applying migration '00000000000000_CreateIdentitySchema'.
Applying migration '00000000000000_CreateIdentitySchema'.
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetRoles" (
          "Id" nvarchar(450) NOT NULL CONSTRAINT "PK_AspNetRoles" PRIMARY KEY,
          "Name" nvarchar(256) NULL,
          "NormalizedName" nvarchar(256) NULL,
          "ConcurrencyStamp" nvarchar(max) NULL
      );
Failed executing DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE "AspNetRoles" (
    "Id" nvarchar(450) NOT NULL CONSTRAINT "PK_AspNetRoles" PRIMARY KEY,
    "Name" nvarchar(256) NULL,
    "NormalizedName" nvarchar(256) NULL,
    "ConcurrencyStamp" nvarchar(max) NULL
);
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'near "max": syntax error'.
   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
   at Microsoft.Data.Sqlite.SqliteCommand.PrepareAndEnumerateStatements()+MoveNext()
   at Microsoft.Data.Sqlite.SqliteCommand.GetStatements()+MoveNext()
   at Microsoft.Data.Sqlite.SqliteDataReader.NextResult()
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader()
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteNonQuery()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
SQLite Error 1: 'near "max": syntax error'.

エラー対応

SQLiteでは、 .HasColumnType("nvarchar(max)"); がエラーとなるので、コメントアウトする。

./Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs(修正の一例)

            modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
                {
                    b.Property<string>("Id")
                        .HasColumnType("nvarchar(450)");

                    b.Property<string>("ConcurrencyStamp")
                        .IsConcurrencyToken();
                        //.HasColumnType("nvarchar(max)"); // コメントアウトする

                    b.Property<string>("Name")
                        .HasColumnType("nvarchar(256)")
                        .HasMaxLength(256);

                    b.Property<string>("NormalizedName")
                        .HasColumnType("nvarchar(256)")
                        .HasMaxLength(256);

                    b.HasKey("Id");

                    b.HasIndex("NormalizedName")
                        .IsUnique()
                        .HasName("RoleNameIndex")
                        .HasFilter("[NormalizedName] IS NOT NULL");

                    b.ToTable("AspNetRoles");
                });

再度DB作成

パッケージコンソールマネージャーを開きコマンドを実行

Update-Database -Context ApplicationDbContext
Done.
PM> Update-Database -Context ApplicationDbContext
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "MigrationId", "ProductVersion"
      FROM "__EFMigrationsHistory"
      ORDER BY "MigrationId";
Microsoft.EntityFrameworkCore.Migrations[20402]
      Applying migration '00000000000000_CreateIdentitySchema'.
Applying migration '00000000000000_CreateIdentitySchema'.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetRoles" (
          "Id" nvarchar(450) NOT NULL CONSTRAINT "PK_AspNetRoles" PRIMARY KEY,
          "Name" nvarchar(256) NULL,
          "NormalizedName" nvarchar(256) NULL,
          "ConcurrencyStamp" TEXT NULL
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetUsers" (
          "Id" nvarchar(450) NOT NULL CONSTRAINT "PK_AspNetUsers" PRIMARY KEY,
          "UserName" nvarchar(256) NULL,
          "NormalizedUserName" nvarchar(256) NULL,
          "Email" nvarchar(256) NULL,
          "NormalizedEmail" nvarchar(256) NULL,
          "EmailConfirmed" bit NOT NULL,
          "PasswordHash" TEXT NULL,
          "SecurityStamp" TEXT NULL,
          "ConcurrencyStamp" TEXT NULL,
          "PhoneNumber" TEXT NULL,
          "PhoneNumberConfirmed" bit NOT NULL,
          "TwoFactorEnabled" bit NOT NULL,
          "LockoutEnd" datetimeoffset NULL,
          "LockoutEnabled" bit NOT NULL,
          "AccessFailedCount" int NOT NULL
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetRoleClaims" (
          "Id" int NOT NULL CONSTRAINT "PK_AspNetRoleClaims" PRIMARY KEY,
          "RoleId" nvarchar(450) NOT NULL,
          "ClaimType" TEXT NULL,
          "ClaimValue" TEXT NULL,
          CONSTRAINT "FK_AspNetRoleClaims_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetUserClaims" (
          "Id" int NOT NULL CONSTRAINT "PK_AspNetUserClaims" PRIMARY KEY,
          "UserId" nvarchar(450) NOT NULL,
          "ClaimType" TEXT NULL,
          "ClaimValue" TEXT NULL,
          CONSTRAINT "FK_AspNetUserClaims_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetUserLogins" (
          "LoginProvider" nvarchar(128) NOT NULL,
          "ProviderKey" nvarchar(128) NOT NULL,
          "ProviderDisplayName" TEXT NULL,
          "UserId" nvarchar(450) NOT NULL,
          CONSTRAINT "PK_AspNetUserLogins" PRIMARY KEY ("LoginProvider", "ProviderKey"),
          CONSTRAINT "FK_AspNetUserLogins_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetUserRoles" (
          "UserId" nvarchar(450) NOT NULL,
          "RoleId" nvarchar(450) NOT NULL,
          CONSTRAINT "PK_AspNetUserRoles" PRIMARY KEY ("UserId", "RoleId"),
          CONSTRAINT "FK_AspNetUserRoles_AspNetRoles_RoleId" FOREIGN KEY ("RoleId") REFERENCES "AspNetRoles" ("Id") ON DELETE CASCADE,
          CONSTRAINT "FK_AspNetUserRoles_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "AspNetUserTokens" (
          "UserId" nvarchar(450) NOT NULL,
          "LoginProvider" nvarchar(128) NOT NULL,
          "Name" nvarchar(128) NOT NULL,
          "Value" TEXT NULL,
          CONSTRAINT "PK_AspNetUserTokens" PRIMARY KEY ("UserId", "LoginProvider", "Name"),
          CONSTRAINT "FK_AspNetUserTokens_AspNetUsers_UserId" FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE CASCADE
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX "IX_AspNetRoleClaims_RoleId" ON "AspNetRoleClaims" ("RoleId");
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE UNIQUE INDEX "RoleNameIndex" ON "AspNetRoles" ("NormalizedName") WHERE [NormalizedName] IS NOT NULL;
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX "IX_AspNetUserClaims_UserId" ON "AspNetUserClaims" ("UserId");
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX "IX_AspNetUserLogins_UserId" ON "AspNetUserLogins" ("UserId");
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX "IX_AspNetUserRoles_RoleId" ON "AspNetUserRoles" ("RoleId");
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE INDEX "EmailIndex" ON "AspNetUsers" ("NormalizedEmail");
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE UNIQUE INDEX "UserNameIndex" ON "AspNetUsers" ("NormalizedUserName") WHERE [NormalizedUserName] IS NOT NULL;
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
      VALUES ('00000000000000_CreateIdentitySchema', '8.0.8');
Done.

作成できた。

投稿日時: 2024-08-27 13:23:27

一般的な値の交換は、一時退避の変数を用意し、値を退避してから
値の入れ替えを行う方法。

var num1 = 5;
var num2 = 6;

var temp = num1; // 値を退避させる。
num1 = num2;
num2 = temp;

変数を増やさずに入れ替えを行う方法として、排他的論理和や加減、乗除による入れ替えもある。 排他的論理和
var num1 = 5; // 101
var num2 = 6; // 110

num2 ^= num1; // 110 => 011 num1 ^= num2; // 101 => 110 num2 ^= num1; // 011 => 101


加減による方法

var num1 = 5;
var num2 = 6;

num2 = num1 - num2;
num1 -= num2;
num2 += num1;

乗除による方法(分母の数字は0以外であること)
var num1 = 5.0;
var num2 = 6.0;

num2 = num1 / num2; num1 /= num2; num2 *= num1;

投稿日時: 2024-07-28 08:54:28
更新日時: 2024-07-28 08:55:28
C#

列挙体を宣言

enum Fruit { Apple, Orange, Kiwi, Banana }

列挙体の文字列を表示

ToString()で設定した文字列を表示

enum Fruit { Apple, Orange, Kiwi, Banana }

Fruit f = Fruit.Apple;
Console.WriteLine(f.ToString()); // Apple

列挙体を配列に変換

GetValuesを使って配列に変換

enum Fruit { Apple, Orange, Kiwi, Banana }
var fruits = (Fruit[])Enum.GetValues(typeof(Fruit));

列挙体の個数

配列に変換してからlengthで取得する方法か・・・

enum Fruit { Apple, Orange, Kiwi, Banana }
var fruits = (Fruit[])Enum.GetValues(typeof(Fruit));
var count = fruits.Length;

列挙体の最後に数を知るための要素を追加しておくか・・・

enum Fruit { Apple, Orange, Kiwi, Banana, Count }
var count = (int)Fruit.Count;

foreachでループ処理

enum Fruit { Apple, Orange, Kiwi, Banana }

var ary = Enum.GetValues(typeof(Fruit));

foreach(var val in ary)
{
    Console.WriteLine("{0}->{1}", (int)val, val.ToString());
}
0->Apple
1->Orange
2->Kiwi
3->Banana
投稿日時: 2024-07-28 07:23:28

StyleCopで解析した結果大量のエラーが出ているものの、自動生成されたプログラム中で発生している場合は、
ファイル先頭に以下を追加し、解析ツールに自動生成されたことを伝える

// <auto-generated />
投稿日時: 2024-07-22 11:14:22
C#

フロント側のWebサーバーで受けたリクエストを
ASP.NET CoreのKestrel Webサーバーで受け取った際に
フロント側でX-Forwarded-For、X-Forwarded-Protoを設定をしておけば
以下で取得できるものかと思っていたのですが、残念ながらフロント側のIPアドレスが取れるだけだったので


this.HttpContext.Connection.RemoteIpAddress.ToString();

フロント側で受けた際のIPアドレスをとるなら以下で取得。


this.HttpContext.Request.Headers["X-Forwarded-For"].ToString();
投稿日時: 2024-07-06 00:15:06
更新日時: 2024-07-06 00:16:06

最近の投稿

最近のコメント

タグ

アーカイブ

その他