87
95

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C#Advent Calendar 2021

Day 14

「C#?Visual Studio要るんでしょ?」からの脱却

Last updated at Posted at 2021-12-13

はじめに

2016年に.NET Coreが誕生し、Windowsのプラットフォーム依存がなくなりました。以前書いた記事に対するコメントで「C#?Visual Studio要るんでしょ?」、「開発を支えるツールがあるの?」など、C#がWindows以外で動くことは知っているけど、実用に耐えるのか?を疑問に思っている人が多いと感じました。私が個人サービスを開発して得た経験や知見を通じてVisualStudioがなくても充分に開発出来るということを知ってもらえたら嬉しいです。ハンズオン形式でご紹介しますので、よろしければ作成したリポジトリをご覧ください。

本記事は.NET5を対象としています。ご了承下さい。

事前準備

.NET Coreを利用するためには.NET SDKと呼ばれる.NETアプリケーションに必要な一連のライブラリとツールが必要です。以下のURLからダウンロードしてください。

環境変数の設定

エラーの原因を探るためにstackoverflow、github issueなど英語の記事を探すことがあります。sdkをインストールするとデフォルトのlocaleが設定され、エラーが日本語で表示されます。日本語では記事が見つかりにくく、トラブルシュートに時間がかかってしまうのでデフォルトのlocaleはen-usに設定することをオススメします。DOTNET_CLI_UI_LANGUAGE 環境変数にen-usを設定すれば英語でエラーが表示されるようになります。

VSCodeの拡張

VSCodeの拡張が充実しているので入れた方が開発体験が上がると思います。最低限必要な拡張機能をインストールすることをオススメします。

C#のVSCode拡張です。デバッグなどC#にまつわる機能をサポートしています。

///と入力するとSummaryのテンプレートを自動生成してくれます。

プロジェクト作成

dotnet newコマンドを用いることで用意されたテンプレートに基づいてプロジェクトを作成することが出来ます。dotnet newと叩くと、以下のようにconsoleアプリからWEB API、SPAと一通りのアプリケーションテンプレートが利用出来ることがわかります。

$ dotnet new
Template Name                                Short Name           Language    Tags
-------------------------------------------  -------------------  ----------  ----------------------
Console Application                          console              [C#],F#,VB  Common/Console
Class library                                classlib             [C#],F#,VB  Common/Library
WPF Application                              wpf                  [C#]        Common/WPF
WPF Class library                            wpflib               [C#]        Common/WPF
WPF Custom Control Library                   wpfcustomcontrollib  [C#]        Common/WPF
WPF User Control Library                     wpfusercontrollib    [C#]        Common/WPF
Windows Forms (WinForms) Application         winforms             [C#]        Common/WinForms
Windows Forms (WinForms) Class library       winformslib          [C#]        Common/WinForms
Worker Service                               worker               [C#],F#     Common/Worker/Web
MSTest Test Project                          mstest               [C#],F#,VB  Test/MSTest
NUnit 3 Test Project                         nunit                [C#],F#,VB  Test/NUnit
NUnit 3 Test Item                            nunit-test           [C#],F#,VB  Test/NUnit
xUnit Test Project                           xunit                [C#],F#,VB  Test/xUnit
MVC ViewImports                              viewimports          [C#]        Web/ASP.NET
Razor Component                              razorcomponent       [C#]        Web/ASP.NET
MVC ViewStart                                viewstart            [C#]        Web/ASP.NET
Razor Page                                   page                 [C#]        Web/ASP.NET
Blazor Server App                            blazorserver         [C#]        Web/Blazor
Blazor WebAssembly App                       blazorwasm           [C#]        Web/Blazor/WebAssembly
ASP.NET Core Empty                           web                  [C#],F#     Web/Empty
ASP.NET Core Web App (Model-View-Control...  mvc                  [C#],F#     Web/MVC
ASP.NET Core Web App                         webapp               [C#]        Web/MVC/Razor Pages
ASP.NET Core with React.js and Redux         reactredux           [C#]        Web/MVC/SPA
ASP.NET Core with Angular                    angular              [C#]        Web/MVC/SPA
ASP.NET Core with React.js                   react                [C#]        Web/MVC/SPA
Razor Class Library                          razorclasslib        [C#]        Web/Razor/Library
ASP.NET Core Web API                         webapi               [C#],F#     Web/WebAPI
ASP.NET Core gRPC Service                    grpc                 [C#]        Web/gRPC

今回はASP.NET Core Web APIを作成します。作成するには以下のコマンドを入力します。

$ dotnet new webapi

カレントディレクトリを見るとASP.NET Core Web APIのテンプレートが作成されています。

$ ls
Controllers                  Startup.cs                   dotnetcore_sample.csproj
Program.cs                   WeatherForecast.cs           obj
Properties                   appsettings.Development.json
README.md                    appsettings.json

.NET SDK のバージョンを管理する

.NET CLIコマンドを実行するときに使う .NET SDK のバージョンを定義します。異なるバージョンのプロジェクトを開発する際などに便利な機能です。バージョンを管理するファイルはglobal.jsonというファイルで以下のコマンドを入力すると作成できます。

$ dotnet new globaljson

するとglobal.jsonという名前で以下のようなファイルが作成されます。ここで使用するsdkのバージョンを固定することができます。

{
  "sdk": {
    "version": "5.0.403"
  }
}

ソリューションファイルの作成

複数のプロジェクトファイルを整理するためにソリューションファイル(以下、sln)というものがあります。slnは必要なときにコマンドで生成するようになっています。プロジェクト作成で使用したnewコマンドを利用してslnを作成します。

$ dotnet new sln

これでslnファイルのテンプレートが作成されました。プロジェクトに紐付ける場合はaddコマンドを入力します。以下は先ほど作成したプロジェクトファイルをslnファイルに追加しています。

$ dotnet sln add dotnetcore_sample.csproj

これで先ほど作成したプロジェクトに紐づきました。

実行

パッケージを取得します。

$ dotnet restore

プロジェクトを実行します。

$ dotnet run

実行するとhttps://localhost:5001で立ち上がるのでcurlコマンドを叩いて結果を確認してみます。

$ curl -s -X GET https://localhost:5001/WeatherForecast  | python -mjson.tool
[
    {
        "date": "2021-12-02T23:16:50.986985+09:00",
        "summary": "Warm",
        "temperatureC": -6,
        "temperatureF": 22
    },![スクリーンショット 2021-12-01 午後11.40.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496802/7c1eacb6-274a-4395-1130-d09fea08bb69.png)

    ...
]

ちゃんとWEB APIが動くことを確認できました。

ちなみにWEB APIテンプレートはSwaggerも導入されていますので、https://localhost:5001/swagger/index.htmlにアクセスするとswaggerの画面が表示されます。
スクリーンショット 2021-12-01 午後11.23.33.png

監視する

dotnet watchコマンドを用いてファイルを監視することで、ファイルが変更されたらビルドをしてくれます。毎回ビルドし直さなくても良くなるので開発効率が上がります。

$ dotnet watch run
watch : Started
Building...

ファイルを変更すると再度ビルドが始まり、変更内容をビルドしてくれます。

watch : Exited
watch : File changed: dotnetcore_sample/Program.cs
watch : Started
Building...

ステップ実行

プロジェクトファイルが存在するディレクトリでVSCodeを開くと以下のダイアログが表示されます。

スクリーンショット 2021-12-01 午後11.37.45.png
Yesを入力すると.vscodeディレクトリが作成されアタッチするためのファイルが作成されます。F5を押下すると実行され、ブレークポイントを貼ってステップ実行が可能となります。

スクリーンショット 2021-12-01 午後11.40.51.png

これで一通りの開発が出来るようになりました。

#Docker
.NET CoreではDockerが利用出来るようになりました。Dockerを用いることで環境依存を無くなり、AWS、GCP、Azureなどホスティングを選定する選択肢が広がります。AWS FargateやGCP Cloud Runなどサーバーレスを用いることで従来のホスティングより費用を抑えることが出来るのも魅力です。個人的には、これが一番強力だと感じています。

ビルド

先ほど作ったWEB APIをビルドします。まずはDockerfileを用意します。DockerfileはMSから公式のサンプルが出ていますので参考にすると良いかもしれません。

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /app

COPY *.sln .
COPY *.csproj .
RUN dotnet restore
RUN dotnet publish -c release -o /out

FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app/out ./
ENTRYPOINT ["dotnet", "dotnetcore_sample.dll"]

ビルドします。パッケージがキャッシュされるため--no-cacheをつけています。

$ docker build --no-cache -t dotnetcore_sample .

実行します。

$ docker run --rm -p 5000:80 dotnetcore_sample

これでlocalhost:5000に先ほど作成したWEB APIが立ち上がりました。

開発体験(DX)が向上するためのツール

Visual Studioがなくても開発出来ることを説明しましたが、ここからは快適に開発するためのツールを紹介します。

##.NETツール
.NETツールとは、NuGetにアップロードされたコンソールアプリケーションをCLI上で実行出来る仕組みです。nodeでいうnpmをイメージしてもらえるとわかりやすいかもしれません。.NETツールのおかげで、今まで煩雑だった.NET系のCLI処理が簡単にかけるようになりました。.NETツールには、どこでもコマンドが叩けるグローバルツール、既定のディレクトリのみ利用できるローカルツールの2種類があります。ここでは、C#で代表的なコードフォーマッタであるdotnet-formatを例にあげて紹介します。個人的にはDockerの次に魅力を感じています:sunglasses:

.NETツールは以下のリンクから探すことが可能です。

グローバルツール

グローバルインストールするには-gオプションを付けます。-gオプションを付けると、どのディレクトリからもコマンドを入力することが可能です。

  • インストール
$ dotnet tool install -g dotnet-format
  • アンインストール
$ dotnet tool uninstall -g dotnet-format
  • アップデート
$ dotnet tool update -g dotnet-format
  • 実行
$ dotnet format

ローカルツール

特定のディレクトリのみ実行する場合は、ローカルツールを使用します。インストールしたツールを管理するマニフェストファイル1が必要です。GitHub ActionsなどのCIで実行する場合は、ローカルツールを利用します。

  • マニフェストファイルを作成する
$ dotnet new tool-manifest

コマンドを実行すると.config/dotnet-tools.jsonが追加され、以下のような内容になっているかと思います。

dotnet-tools.json
{
  "version": 1,
  "isRoot": true,
  "tools": {}
}

これでマニフェストファイルの雛形が完成しました。次はマニフェストファイルにツールをインストールします。

  • インストール
$ dotnet tool install dotnet-format

これでローカルにインストールされ、先ほど作成した.config/dotnet-tools.jsondotnet-formatが追加されています。

{
   "version": 1,
   "isRoot": true,
+  "tools": {
+    "dotnet-format": {
+      "version": "5.1.250801",
+      "commands": [
+        "dotnet-format"
+      ]
+    }
+  }
 }

これでローカルツールを利用する準備が整いました。あとはパッケージを取得し、実行するだけです。

  • パッケージを取得する
$ dotnet tool restore
  • フォーマットする
$ dotnet format

特定のディレクトリのプロジェクトに対してdotnet formatが適応されました。

コードフォーマッター

開発体験を向上するにはコードフォーマッタがあると便利です。.NETツールで紹介したdotnet-formatを用いて、VSCodeの連携を含めてご紹介します。dotnet-format の使い方は.NETツールの章で説明させて頂きましたので、ここでは説明を省きます。

.NET6 SDKからdotnet formatが標準で組込みされるようになりました。.NET6を利用する方は.NETツールでのインストールは不要です

.editorconfigを作成する

.editorconfigがなくともフォーマットされますが、細かい設定が可能となるので作っておいた方が良いと思います。roslyn2の.editorconfigが公開されているので、この設定を持ってくるのが楽ですし、天下のroslynなので間違いはないかと思います。

VSCodeの保存と同時にフォーマットする

Ctrl + Sなどの保存と同時にフォーマットすると非常に開発が楽になります。

  • 左下の歯車のアイコン→設定をクリックします
スクリーンショット 2021-12-08 午前0.22.08.png
  • フォームに「format」と入力します
スクリーンショット 2021-12-08 午前0.23.21.png
  • Format On Saveにチェックします
スクリーンショット 2021-12-08 午前0.24.19.png
  • フォームに「csharp」と入力します
スクリーンショット 2021-12-08 午前0.26.23.png
  • settings.jsonで編集をクリックします
    スクリーンショット 2021-12-08 午前0.26.03.png

  • [csharp]に以下の設定を追記します

"[csharp]": {
+  "editor.defaultFormatter": "ms-dotnettools.csharp",
},

これで、保存時にC#のソースコードがフォーマットされるようになりました。

静的コード解析

品質の高いコードを書くために、静的解析は必須です。C#にはさまざまな静的コード解析がありますが、ここでは私のオススメするRoslynatorを紹介します。なぜRoslynatorがオススメかというと、モダンな解析、CLIが充実しており、後述するCIに組み込むことが可能となります。

以前は、解析失敗してもexitコードが0で終わるというバグがあったため、CIに組み込む場合は自作のシェルスクリプトなど書く必要がありましたが、「dotnet format --checkと同じにして〜〜」とわがままを言ったところ、改善していただけました:pray:感謝

CLIでの.NET6はまだサポートされていません。issueに上がっており、対応前提で動いてくれてますので気長に待ちましょう(貢献するのもよいかも!)

インストール

.NETツールをインストールします。

$ dotnet tool install roslynator.dotnet.cli

パッケージ追加

解析対象のプロジェクトにパッケージを追加します。

$ dotnet add package Roslynator.Analyzers

解析

以下のコマンドで解析、修正が出来ます。解析はレベルがあり--severity-levelを使うと厳密に解析するか指定出来ます。

# 診断
$ dotnet roslynator analyze dotnetcore_sample.sln

# より厳密な解析
$ dotnet roslynator analyze dotnetcore_sample.sln --severity-level=hidden

# 修正
$ dotnet roslynator fix dotnetcore_sample.sln

先ほど作成したプロジェクトを実行すると以下のように解析結果が表示されます。

$ dotnet roslynator analyze dotnetcore_sample.sln
Load solution 'dotnetcore_sample/dotnetcore_sample.sln'
Done loading solution 'dotnetcore_sample/dotnetcore_sample.sln'
Analyze solution 'dotnetcore_sample/dotnetcore_sample.sln'
Analyze 'dotnetcore_sample' 1/1
  Program.cs(22,17): info RCS1021: Use expression-bodied lambda.
  Startup.cs(32,13): info RCS1021: Use expression-bodied lambda.
  Startup.cs(54,13): info RCS1021: Use expression-bodied lambda.
  Startup.cs(29,1): info RCS1036: Remove redundant empty line.
  Program.cs(12,18): warning RCS1102: Make class static.
Done analyzing solution 'dotnetcore_sample/dotnetcore_sample.sln' in 00:02.30

3 RCS1021 Convert lambda expression body to expression-body.
1 RCS1036 Remove redundant empty line.
1 RCS1102 Make class static.

5 diagnostics found

VSCode拡張

VSCode拡張を入れると、開発中も解析してくれます。便利ですね〜〜:kissing_closed_eyes:

スクリーンショット 2021-12-10 午後11.51.30.png

CIを導入する

コードの品質やデプロイを自動化するにはCIの導入が必要です。ここまでご紹介したツールを組み合わせてGithub Actionsを動かすサンプルをご紹介します。Github ActionsでなくともCodeBuildなどどのCIでも同じように動かすことが可能です。

ワークフローを追加する

mainブランチへのプルリクエストがトリガーとなるワークフローを追加します。github Actionsはlinuxが一番コスパが良いのでubuntuで実行しています。以下の例は、単純にビルド、フォーマット、解析のコマンドを羅列しているだけなので、ここまで記事を見ていただけたらわかると思います:relaxed:

.github/workflows/pull_request.yml
name: .NET Core

on:
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: setup checkout
      uses: actions/checkout@v2
      with:
        submodules: true
    - name: setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.403
    - name: restore project
      run: dotnet restore
    - name: build project
      run: dotnet build --no-incremental
    - name: restore .NET Tool
      run: dotnet tool restore
    - name: restore .NET Tool
      run: dotnet tool restore
    - name: analyze code
      run: dotnet roslynator analyze --severity-level=hidden
    - name: format code 
      run: dotnet format --check

プルリクエストを出す

プルリクエストを出すと、CIが動きチェックします。下記の例はチェックに失敗しています。
スクリーンショット 2021-12-10 午後11.07.43.png

「Details」をクリックし、詳細を確認します。

スクリーンショット 2021-12-10 午後11.09.41.png

静的コード解析が失敗したことにより、CIが落ちたことがわかります。

このようにCIが失敗したらマージさせないなどコードの品質向上するための仕組みを構築することが出来ました。

まとめ

以前書いた記事ではVisualStudioを使わないとどれだけ不便なのか、それとも問題ないのかをお伝え出来なかったので、一通り開発で必要なものを選別して書かさせていただきました。Windowsに依存するものでなければある程度、同じように開発出来ることがわかっていただけたかと思います。私個人としては、C#がマルチプラットフォームで開発出来ることを夢見てたので、とても感慨深いものがありますし、おそらく同じような方は多いのではないかと思います。とにかく.NET系は破壊的変更が少なく、継続して開発しやすい素晴らしいフレームワークだと思いますのでC#perも他の言語を触れていた人も含め、本記事で興味を持っていただければ幸いです:bow_tone1:

  1. ローカル アクセス専用の.NETツールを管理するファイル

  2. C#及びVisual Basic .NETのフリーかつオープンソースのコンパイラ・コード解析API

87
95
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
87
95

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?