.NET Core と VS Code によるクロスプラットフォーム Web 開発環境

.NET Core が成熟してきているというのをヒシヒシと感じていて、元祖 .NET Framework はメンテナンスモードだし、.NET Core ファミリーは来年には .NET 5 として大統一されるみたいだし、ちょっと触っておきたいという気持ちになりました。

devblogs.microsoft.com

Visual Studio 2019 Community の最新版を入れていても、.NET Core SDK の最新版は入っていないっぽいので、ダウンロード・インストールします。

dotnet.microsoft.com

Windows 版

f:id:kondoumh:20190828085947p:plain

C:\> dotnet --info
.NET Core SDK (global.json を反映):
 Version:   2.2.401
 Commit:    729b316c13

ランタイム環境:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.2.401\

Host (useful for support):
  Version: 2.2.6
  Commit:  7dac9b1b51

.NET Core SDKs installed:
  2.1.503 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.401 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download

macOS 版

f:id:kondoumh:20190828225609p:plain

$  dotnet --info
.NET Core SDK (global.json を反映):
 Version:   2.2.401
 Commit:    729b316c13

ランタイム環境:
 OS Name:     Mac OS X
 OS Version:  10.14
 OS Platform: Darwin
 RID:         osx.10.14-x64
 Base Path:   /usr/local/share/dotnet/sdk/2.2.401/

Host (useful for support):
  Version: 2.2.6
  Commit:  7dac9b1b51

.NET Core SDKs installed:
  1.0.4 [/usr/local/share/dotnet/sdk]
  2.0.0 [/usr/local/share/dotnet/sdk]
  2.1.505 [/usr/local/share/dotnet/sdk]
  2.1.701 [/usr/local/share/dotnet/sdk]
  2.2.401 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.9 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.6 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.9 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.6 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 1.0.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 1.1.2 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.6 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download

バージョンや runtime 同じですね。

docs.microsoft.com

チュートリアルを Visual Studio ではなく、dotnet コマンドと VS Code で進めていきます(チュートリアルには両方の手順が書かれています)。

dotnet コマンドリファレンスは↓にあります。

docs.microsoft.com

空のソリューションを作成します。

$ mkdir WebApiSample
$ cd WebApiSample
$ dotnet new sln
テンプレート "Solution File" が正常に作成されました。
$ ls
WebApiSample.sln

WebApi のプロジェクトを作成します。

$ dotnet new webapi -o TodoApi
テンプレート "ASP.NET Core Web API" が正常に作成されました。

作成後のアクションを処理しています...
'dotnet restore' を TodoApi/TodoApi.csproj で実行しています...
  TodoApi.csproj の復元が 1.51 sec で完了しました。

正常に復元されました。

$ ls
TodoApi          WebApiSample.sln

ソリューションに TodoApi プロジェクトを追加してビルドします。

$ dotnet sln WebApiSample.sln add TodoApi/TodoApi.csproj
プロジェクト `TodoApi/TodoApi.csproj` をソリューションに追加しました。

$ dotnet build
.NET Core 向け Microsoft (R) Build Engine バージョン 16.2.32702+c4012a063
Copyright (C) Microsoft Corporation.All rights reserved.

  WebApiSample/TodoApi/TodoApi.csproj の復元が 48.07 ms で完了しました。
  TodoApi -> WebApiSample/TodoApi/bin/Debug/netcoreapp2.2/TodoApi.dll

ビルドに成功しました。
    0 個の警告
    0 エラー

経過時間 00:00:03.44

以上、.NET Core SDK に含まれる dotnet コマンドだけで、ソリューション作成、プロジェクトの作成と追加、ビルドまでできてしまいました。

ここで VS Code でプロジェクトフォルダを開くと拡張のインストールを促されます。

marketplace.visualstudio.com

これは OmniSharp が開発する Language Server Protocol 準拠の拡張で、本家 Visual Studio レベルのコード補完、エラー解析機能を提供します。その他必要なソフトウェアが一式インストールされます。

チュートリアルに従ってコードを追加していきます。

データクラス

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Entity Framework の DbContext

using Microsoft.EntityFrameworkCore;

namespace TodoApi.Models
{
  public class TodoContext : DbContext
  {
    public TodoContext(DbContextOptions<TodoContext> options) : base(options)
    {
    }
    public DbSet<TodoItem> TodoItems { get; set; }
  }
}

これを Startup クラスに DbContext としてインジェクションします。UseInMemoryDatabase なんての使えるので開発が楽ですね。

using Microsoft.EntityFrameworkCore;
 :
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
        :
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
            opt.UseInMemoryDatabase("TodoList"));
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }
       :
    }
}

あとは、Controller クラスに HTTP メソッドに対応するメソッドを追加していきます。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;

namespace TodoApi.Controllers
{
  [Route("api/[controller]")]
  [ApiController]
  public class TodoController : ControllerBase
  {
    private readonly TodoContext _context;

    public TodoController(TodoContext context)
    {
      _context = context;

      if (_context.TodoItems.Count() == 0)
      {
        _context.TodoItems.Add(new TodoItem { Name = "Item1"});
        _context.SaveChanges();
      }
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
    {
      return await _context.TodoItems.ToListAsync();
    }
  
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
    {
      var todoItem = await _context.TodoItems.FindAsync(id);
      if (todoItem == null)
      {
        return NotFound();
      }
      return todoItem;
    }

    [HttpPost]
    public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem item)
    {
      _context.TodoItems.Add(item);
      await _context.SaveChangesAsync();
      return CreatedAtAction(nameof(GetTodoItem), new TodoItem{ Id = item.Id }, item);
    }
  }
}

VS Code によるコード補完の様子。

f:id:kondoumh:20190829201524p:plain

デバッグの様子です。

f:id:kondoumh:20190829200536p:plain

Windows でも macOS でも全く同じ DX です。Node.js + TypeScript で Express 開発の時も相当いいと思いましたが、もはやほぼ Visual Studio (≠ Code) です。C# のような静的型付け言語には専用の IDE が要ると思っていたのですが・・。サーバーサイドのコードだけならもう Visual Studio なくてもできます。

blog.kondoumh.com

.NET Core SDK と VS Code があれば、快適な ASP.NET Core 開発環境が構築できることが分かりました。

さらに Remote Development を使えばリモートマシンやコンテナイメージに .NET Core SDK を導入しておき、手元は VS Code だけで開発ってのもできるでしょう。

blog.kondoumh.com

OmniSharp に対応した拡張を使えば、ターミナル の Emacs や Vim でもいけそうです。