お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

ASP.NET Core 3.0+Blazorで詰まったとこ

この記事の目的

ASP.NET Core 3.0とBlazorを使っていい感じのサーバーを作ろうとしているので気づいたことを適宜メモしていくための記事

コントローラークラスの作成

    [Route("api/[controller]")]
    [ApiController]
    public class HogeController : ControllerBase
    {
        private FugaContext Context { get; }
        public HogeController(FugaContext context)
        {
            Context = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Hoge>>> GetHoge()
        {
            return await Context.Hoges.ToListAsync();
        }
    }

コントローラークラスはこんな感じのやつ

Startup.csに

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

これが書いてあればhttps://localhost:99999/api/HogeとかでAPIにアクセスできる

選択したコードジェネレータを実行中にエラーが発生しました

Scaffolding failed to edit Startup class to register the new Context using Dependency Injection.
Make sure there is a Startup class and a ConfigureServices method and Configuration property in it.

Blazor(ASP.NET Core hosted)のプロジェクトを作ってサーバー側のプロジェクトに適当なコントローラーをスキャフォールディングしようとしたら怒られたエラー。

これはStartup.csクラスにConfigurationというプロパティが無いので怒られている。
どうやらこのへんのスキャフォールディングはパターンマッチ的なことがされていて、Startupクラスとかが正しい形でないと怒られるみたいだ。
幸い、Blazor(サーバーサイド)のプロジェクトを作るとConfigurationとかがちゃんと書かれたStartupクラスが吐かれるのでそれを参考にするとちゃんとスキャフォールディング(いちいち書くのめんどくさい……)できた。

Blazor(ASP.NET Core hosted)のこういうコードを

public class Startup
{
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        services.AddResponseCompression(opts =>
        {
            opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "application/octet-stream" });
        });
    }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseResponseCompression();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBlazorDebugging();
        }

        app.UseClientSideBlazorFiles<Client.Startup>();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
        });
    }
}

下記のように書き直してやろう(ダジャレじゃないよ)

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        services.AddResponseCompression(opts =>
        {
            opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "application/octet-stream" });
        });
    }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseResponseCompression();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBlazorDebugging();
        }

        app.UseClientSideBlazorFiles<Client.Startup>();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
        });
    }
}

あくまで個人的な感想だが、最近のC#(というかASP.NETってそういうもんなのか?)はパターンマッチに傾倒しすぎてて、「型安全!」「安心!!」みたいなプログラマに優しい言語からだんだんと離れて行ってしまってるような気がしてう~~ん……。

データベースへのアクセス

何も考えずにコントローラーをスキャフォールディングすると、ついでに作るDbContextへのConnectionStringがappsettings.jsonに追加される。
このConnectionStringはいい感じのローカルSQLサーバーへの接続を表している。が、このままだとコントローラーへAPIで問い合わせてもSQLサーバーに入れませんでしたというエラーが出る。
なので初めにまずマイグレーションというやつをして、次にそのマイグレーションを使ってデータベースを初期化しないといけない。
それをするにはプロジェクトのディレクトリへ行き、Power shellとかで次のようなコマンドを打つ。

dotnet-ef migrations add initial-migration //多分、いい感じのマイグレーションをいい感じに生成する魔法の言葉
dotnet-ef database update          //多分、マイグレーションを使ったデータベースの初期化

ところでマイグレーションってなんだ……?

injection

テンプレートで生成されたrazorページの中を見ている「@injection hogehoge」というのがあった。
調べてみるとこれを使うことでStartup.csの中で登録したシングルトンにアクセスできるらしい。
詳しくは
ASP.NET Core でのビューへの依存関係の挿入 | Microsoft Docs
とか
ASP.NET Core MVC を使ってドロップダウンリストをいい感じに実装する - しばやん雑記
とか。

とにかく
Startup.csに

public void ConfigureServices(IServiceCollection services)
{
    ~~
    services.AddSingleton<HogeSingleton>();
    ~~
}

と書いておけば

@injection HogeSingleton hogeSingleton

みたいにしてrazorファイルからアクセスできる。

Html.DisplayFor、Html.EditorFor

あるインスタンスをいい感じに編集できるような機能はないかなあと思っていたところ、これまたテンプレートで「Entity Frameworkを使用するRazorページ(CRUD)」を作ってみたらIndex.cshtmlの中に書いてあった。
DisplayForはその名の通りそのインスタンスをいい感じに表示する関数で、EditorForはそれが編集できるようになってるもの。
クラスのプロパティとかにアトリビュートで[DisplayName("hogehoge")]とか書けばある程度は指定もできるみたいだ。
このページが詳しい。
第6回 テンプレート機能でビュー開発を効率化(1/3) - @IT