C#のgRPCは、他の言語と手順の形がだいぶ違います。protocを自分で叩く場面がほとんどなく、csprojに1行足せばビルドのたびにスタブが出てくるからです。そのぶん、詰まる場所が生成工程から配置工程へ移ります。この記事では2026年9月時点の一次情報をもとに、パッケージの選び方、csprojでの生成指定、サーバーとクライアントの組み立て、IISやAzure App Serviceで通るかの判定までを実装順に並べます。プロトコルの仕組みそのものはgRPCの仕組みとRESTとの違いに譲るのが本記事の役割です。
まとめ|C#で先に決める3点と落ちやすい1点
着手前に決めるのは3つです。protoファイルをサーバー側に置くか共有リポジトリへ切り出すか、csprojのGrpcServices属性をどう振り分けるか、本番のWebサーバーをKestrel単体にするかIISの背後に置くか。3つ目は後から変えるとOSの要件まで巻き戻るため、設計の初日に決めます。
落ちやすいのは配置です。ローカルのdotnet runは開発証明書つきのTLSエンドポイントが自動で立つため設定なしでも動きますが、同じコードをIISの背後やAzure App Serviceへ置くと、OSのビルド番号やプランの種別で通らないことがあります。
結論を先に置きます。2026年9月時点で新規に組むなら、.NET 10(10.0.11・LTS)とGrpc.AspNetCore 2.83系によるKestrel単独ホストが素直です。IISへ相乗りさせたい要件があるなら、Windows Server 2022ビルド20348以降かを先に確認します。
パッケージ構成|Grpc.AspNetCoreとGrpc.Net.Clientの役割
.NETのgRPCは、サーバー側とクライアント側で入れるパッケージが分かれています。混乱しやすいのは、サーバー側がメタパッケージ1つで済むのに対し、クライアント側は3つを自分で並べる点です。取り違えると、スタブが生成されず型が見つからないというビルドエラーから始まります。
Grpc.AspNetCoreの中身とクライアント側で追加する3つの参照
サーバー側はGrpc.AspNetCoreを1つ入れれば済みます。このメタパッケージが、実行時のASP.NET Core統合と、ビルド時にスタブを生成するGrpc.Toolsへの参照をまとめて引き込むためです。クライアント側はGoogle.Protobuf、Grpc.Net.Client、Grpc.Toolsの3つを個別に並べます。
2026年9月2日にNuGetで確認した版は、gRPC側の4パッケージがいずれも2.83.0、Google.Protobufが3.36.1でした。番号が揃って動くため、まとめて上げる運用にできます。
| パッケージ | 役割 | 入れる側 |
|---|---|---|
| Grpc.AspNetCore | サーバー統合のメタ | サーバー |
| Grpc.Net.Client | チャネルと呼び出し | クライアント |
| Grpc.Tools | ビルド時のコード生成 | 両方 |
| Google.Protobuf | メッセージの直列化 | 両方 |
| Grpc.AspNetCore.Web | gRPC-Web対応の追加 | サーバー |
クライアント側のGrpc.ToolsにはPrivateAssetsをallで付けます。ツールはビルド時にしか働かないためで、忘れると参照元の別プロジェクトへツール依存が伝播していきます。
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.36.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.83.0" />
<PackageReference Include="Grpc.Tools" Version="2.83.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Grpc.Coreがメンテナンスモードに入った経緯と移行の判断
検索して出てくるC#の記事には、Grpc.Coreを使った書き方が今も残っています。C言語のコアライブラリをP/Invoke経由で叩く古い実装です。2026年9月2日時点でNuGetのGrpc.Coreは2.46.6が最終版で、説明文自体に「in maintenance mode and will be replaced by grpc-dotnet」と書かれています。
既存資産がGrpc.Coreで動いている場合、移行の順序はクライアント側からです。呼び出しコードは生成型のAPIがほぼ同じ形のため、チャネル生成をGrpcChannelへ差し替えれば通ることが多くなります。サーバー側は起動コードをASP.NET Coreのホストへ書き直す作業になるため、別リリースに分けます。
protoからの生成|csprojのProtobuf項目とGrpcServicesの指定
C#の生成工程は、他の言語のようにコマンドを組み立てる作業ではありません。csprojの<Protobuf>項目にファイルを登録すると、Grpc.Toolsがビルドのたびに必要な分だけ生成します。MSBuildの依存解決に乗るため、dotnet buildを叩けば生成物も更新される仕組みです。
GrpcServicesをServerとClientで切り分ける判断の基準
<Protobuf>参照は、既定では具象クライアントとサービス基底クラスの両方を出します。GrpcServices属性を書かなかったときの既定値がBothだからです。指定できる値はBoth・Server・Client・Noneの4つで、サーバープロジェクトならServer、呼び出す側ならClientを明示します。
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
基準はシンプルで、そのプロジェクトが使う側だけを出します。Bothを残すと使わない型が混ざり、ビルド時間が無駄に伸びるためです。Noneはメッセージ専用のprotoを共有ライブラリへ置く場合に使います。
C#固有の話としてはoption csharp_namespaceがあり、書かないとpackage名からパスカルケースの名前空間が推測されます。命名規約に合わせたいなら明示します。記法そのものはProtocol Buffersのファイル設計と採用判断にまとめました。
生成物がobjディレクトリへ出る設計と共有protoの置き場所
生成されたC#ファイルはobjディレクトリの下に出ます。ドキュメントも、生成資産はソース管理へチェックインされないビルド成果物だと明記しています。コミットするか毎回生成するかで悩む余地が、設計として排除されているわけです。
副作用もあります。IDEでスタブの定義へ飛ぶとobj配下のファイルが開くため、直接編集する事故が起きがちです。生成物は次のビルドで上書きされるので、編集は派生クラス側で行う前提を共有しておきます。
共有protoの置き場所は、サーバーとクライアントが別ソリューションになる時点で決めます。Link属性で表示位置を指定できるため、物理配置とプロジェクト構成は分けられます。
<Protobuf Include="..\..\contracts\greet.proto"
Link="Protos\greet.proto"
GrpcServices="Client" />
サーバー実装|AddGrpcとMapGrpcServiceで組む最小構成
サーバー側は、DIへの登録とルーティングへの割り当ての2行で立ち上がります。AddGrpcで機能を有効にし、MapGrpcServiceでサービス実装をエンドポイントへ結びつける形です。ミドルウェアパイプラインにそのまま乗るため、認証やログは既存の仕組みを流用できます。
基底クラスの継承とServerCallContextから取れる情報
protoでservice Greeterを定義すると、抽象型のGreeter.GreeterBaseが生成されます。実装側はこれを継承し、対応するメソッドをオーバーライドする形です。DIはコンストラクタ注入がそのまま効き、.NET 8以降ならプライマリコンストラクタの記法も使えます。
public class GreeterService(ILogger<GreeterService> logger)
: Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(
HelloRequest request, ServerCallContext context)
{
logger.LogInformation("peer={Peer}", context.Peer);
return Task.FromResult(new HelloReply
{
Message = $"Hello {request.Name}"
});
}
}
第2引数のServerCallContextが、HTTP/2メッセージ側の情報を取る窓口です。呼び出し元、リクエストヘッダー、デッドラインの残り、キャンセルトークンを読めます。長く走る処理ではcontext.CancellationTokenを下流へ渡すと、切断時にDBクエリまで止められます。
ただしServerCallContextはASP.NET CoreのAPI全体を見せてはくれません。認証済みのUserプリンシパルなどHttpContextそのものが必要ならGetHttpContext拡張メソッドで取ります。
MVCや最小APIと同じアプリで同居させるときの構成上の注意
gRPCサービスはASP.NET Coreのルーティングパイプラインを共有します。同じアプリの中でMVCコントローラーや最小APIのエンドポイントを並べても干渉しません。ヘルスチェック用のHTTPエンドポイントを1本だけ生やす構成が素直に書けます。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.MapGet("/healthz", () => Results.Ok("ok"));
app.Run();
注意するのはプロトコルの設定です。HTTP/1.1のリクエストも同じポートで受けるにはALPNネゴシエーションが要るため、平文で両方を受けることはできません。
エラーの返し方が別系統になる点も押さえます。MVC側の例外フィルタでHTTPステータスを組み立てても、gRPC呼び出しには届きません。gRPC側はRpcExceptionでStatusCodeを返す形になり、対応関係はgRPCのステータスコードとHTTPの対応で整理しました。
クライアント実装|GrpcChannelの寿命とクライアントファクトリ
クライアント側の中心はGrpcChannelです。ForAddressでアドレスを渡してチャネルを作り、生成されたクライアント型に食わせます。単項メソッドには同期版と非同期版の両方が生成され、SayHelloが同期、SayHelloAsyncが非同期という命名です。
チャネルを使い回す理由とAddGrpcClientでDIへ載せる判断
チャネルは呼び出しのたびに作るものではありません。内部でHttpClientとコネクションを抱えており、生成のたびにTCPとTLSのハンドシェイクが走るからです。呼び出しごとに使い捨てるサンプルが多いのは、あくまで単発のコンソール例だからだと考えてください。
常駐アプリではGrpc.Net.ClientFactoryを入れ、AddGrpcClientでDIコンテナへ登録します。HttpClientFactoryの仕組みに乗るため、ハンドラの寿命管理がそのまま使えます。
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://inventory.internal:5001");
});
判断の基準は寿命です。プロセスが生きている間ずっと同じ相手を呼ぶならファクトリ経由に寄せ、バッチのように一度だけ呼んで終わるならForAddressで作って破棄する形で足ります。
デッドラインと再試行の設定をどこへ置くかとエラーの受け取り方
デッドラインは呼び出し単位で指定します。チャネル側にまとめて設定する仕組みはなく、CallOptionsか生成メソッドの引数で渡す形です。指定しなければ無期限になります。
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "svc-order" },
deadline: DateTime.UtcNow.AddSeconds(3));
再試行はチャネル側の責務です。GrpcChannelOptions.ServiceConfigにRetryPolicyを置けば、再送の判断をライブラリ側へ寄せられます。UNAVAILABLEは再送してよく、INVALID_ARGUMENTは何度投げても結果が変わりません。
呼び出しが失敗するとRpcExceptionが飛び、ex.StatusCodeで分岐できます。落とし穴は、デッドライン超過がOperationCanceledExceptionではなくStatusCode.DeadlineExceededで届く点です。
ホスティング制約|Kestrel・IIS・App Serviceで通る条件
ここからが、日本語の入門記事でほとんど触れられていない領域です。組み込みサーバーはKestrel・TestServer・IIS・HTTP.sysの4つがサポート対象ですが、後ろ2つにはOSのビルド番号という条件が付きます。
TLSなしでHTTP/2を通すListenOptionsの指定と失敗の型
Kestrelのエンドポイントは、既定でHTTP/1.1とHTTP/2の両方を受けます。どちらで話すかはTLSのALPNハンドシェイクで決まる仕組みです。開発時に設定なしで動くのは、TLSエンドポイントが自動で立ちHTTP/2が選ばれるからです。
問題は、TLSを終端するロードバランサの背後で平文の内部通信をしたい場合です。平文エンドポイントにはネゴシエーションの手段がなく、既定でHTTP/1.1に倒れるため、gRPCの呼び出しだけが失敗します。回避するには、そのエンドポイントのプロトコルをHTTP/2固定にします。
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenLocalhost(5080, o =>
o.Protocols = HttpProtocols.Http2);
});
ここでHttpProtocols.Http1AndHttp2を指定すると通りません。複数プロトコルを持つエンドポイントは、ネゴシエーションがないためTLSなしでは使えないからです。TLSありの環境で動いていたものが、コンテナ化やLB導入のタイミングで一斉に落ちる形で表面化します。
IISとHTTP.sysのOS要件とApp ServiceのLinux条件
IISとHTTP.sysでgRPCサービスをホストするには、.NET 5以降に加えて、Windows 11ビルド22000以降またはWindows Server 2022ビルド20348以降が要ります。Windows Server 2019の既存基盤には、そのままでは載りません。IIS側もTLSとHTTP/2を使う構成が必要です。
Azureへ置く場合は、Azure Kubernetes Service、Azure Container Apps、Azure App Serviceが選択肢です。ただしApp ServiceのgRPCにはLinuxベースの環境が必要で、Windows前提で見積もった案件は周辺のデプロイ手順まで作り直しになります。
| ホスト | 条件 | 双方向配信 |
|---|---|---|
| Kestrel | TLSとHTTP/2 | 可 |
| IIS | Server 2022以降 | 不可 |
| HTTP.sys | Server 2022以降 | 可 |
| App Service | Linux環境のみ | 不可 |
| AKS | 追加要件なし | 可 |
表の右列にある通り、Azure App ServiceとIISでホストしたgRPCサービスは双方向ストリーミングをサポートしません。設計段階で双方向を前提にしていると、配置先の決定で仕様を削ることになります。4方式の使い分けはgRPCストリーミングの4方式と切断対策で整理しました。
gRPC-Webへの退避|ブラウザ向けに残す構成と落ちる機能
ブラウザからHTTP/2のgRPCサービスを直接呼ぶことはできません。フロントエンドがgRPCの契約をそのまま使いたいなら、gRPC-Webへ落とす構成を用意します。ここは.NETが他の言語より手軽な領域です。
Grpc.AspNetCore.Webでプロキシを立てずに済ませる構成
選択肢は2つです。Envoyプロキシで変換する方法と、Grpc.AspNetCore.Webのミドルウェアをアプリ自身へ組み込む方法があります。すでにEnvoyが前段にある環境ならそちらへ寄せてもよく、ASP.NET Coreだけで完結させたいなら後者を選びます。
サービス実装には手を入れません。変更は起動コードだけで、ミドルウェアをルーティングの後へ差し込みます。
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
すべてのサービスで既定にしたいなら、UseGrpcWebにGrpcWebOptionsのDefaultEnabledを渡せば個別のEnableGrpcWebが不要です。仕組み側の詳細と他言語サーバーでプロキシが要る理由はgRPC-Webの仕組みとプロキシ構成にまとめています。
双方向ストリーミングが落ちる組み合わせとCORSの露出ヘッダー
gRPC-Webにすると失われる機能があります。ブラウザクライアントからはクライアントストリーミングと双方向ストリーミングを呼べません。.NETクライアントでもHTTP/1.1経由ではこの2つが通らないため、単項メソッドとサーバーストリーミングだけで組める設計に寄せておくのが実務的な落とし所です。
同じ制約はクライアント側でも起こります。FlutterでWeb版ビルドを含める場合、チャネルがgRPC-Webへ切り替わるため使える通信方式が同じ2つに絞られます。分岐の吸収の仕方はgRPC Flutterの実装とWeb版の制約で整理しています。
別ドメインから呼ぶならCORSの設定も要ります。見落とされがちなのが露出ヘッダーで、WithExposedHeadersにGrpc-Status・Grpc-Message・Grpc-Status-Details-Binなどを並べておかないと、ブラウザ側がステータスを読めません。成功時は動くのにエラー内容だけ取れない症状になります。
Blazor WebAssemblyから呼ぶ場合は、Grpc.Net.Client.Webを入れてGrpcWebHandlerをチャネルに差します。あわせて、生成された同期メソッドを呼ばないよう徹底してください。同期版を呼ぶとアプリが応答しなくなるためです。採用判断そのものはBlazorの将来性と.NET 10時代の判断で扱いました。
採用の境界|C#でgRPCを標準にする条件とRESTへ戻す場面
C#のgRPCを社内標準に据える3つの条件と前提をそろえる基準
標準化してよいのは、次の3つが揃うときです。第1に、通信の当事者が自社の管理下にあること。第2に、配置先がKestrel単独かAKS、Container Appsであること。IISやWindows版のApp Serviceが混じると、前章の制約に設計が縛られます。第3に、.NETのバージョンを揃えられることです。現行LTSの.NET 10はサポート終了が2028年11月14日で、ここへ寄せれば更新の足並みも揃います。
前提を揃える基準は3点です。protoの正本を1か所に決めること、生成物をコミットしない運用を全員が理解していること、スキーマ互換の検査をCIへ置くことが最低ラインになります。
RESTやgRPC-Webへ寄せる3場面と判断を戻すときの基準
逆に、gRPCを見送るか併設に留める場面もあります。ひとつは、外部の不特定多数へAPIを公開する場合です。相手側にprotoとコード生成を要求することになり、導入の摩擦が跳ね上がります。ふたつめは、既存のREST APIに多数のクライアントがぶら下がっている場合で、内部だけgRPCにしてREST面を残すほうが移行コストは小さく収まります。3つめは、配置先の制約で双方向ストリーミングが使えず、それでもリアルタイム性が要件にある場合です。
REST面を残しつつ内部をprotoで統一したいなら、protoから逆にRESTを生やす手もあります。その方式と生成構成はgRPC-GatewayでprotoからRESTを生やす構成で扱いました。.NETにはgRPC JSONトランスコーディングという選択肢もあり、どちらを使うかは既存クライアントの数で決めます。
受託開発でproto所有と.NET版をそろえる契約時の取り決め
受託の現場では、技術的な判断より先に決めておくことがあります。protoの所有と改定権をどちらが持つか、.NETの版上げ追随を保守の範囲に含めるかどうかです。生成物がobj配下のビルド成果物である以上、納品するソースコードに生成コードは含まれません。
スキーマの改定手順も先に決めます。フィールド追加はどちらの判断で進めてよいか、削除や番号の付け替えは合意事項にするかという線引きです。接続設計から実装まで含めて相談したい場合は、API開発・システム連携で個別の要件に応じた進め方をご案内しています。
よくある質問
Grpc.CoreからGrpc.Net.Clientへ今すぐ移行すべきですか?
新規コードはGrpc.Net.Clientで書きます。既存資産が即座に壊れるわけではありませんが、最終版が2.46.6で止まっており、説明文にもメンテナンスモードである旨が書かれています。移行はクライアント側のチャネル生成から先に進めてください。
生成されたC#コードはリポジトリにコミットすべきですか?
コミットしません。Grpc.Toolsが出すファイルはobj配下のビルド成果物で、ドキュメント上もソース管理へチェックインしないものと位置づけられています。GoやPythonと違い、.NETではこの点が固定です。
IISの背後でgRPCサービスをホストできますか?
条件付きでできます。.NET 5以降に加えて、Windows 11ビルド22000以降またはWindows Server 2022ビルド20348以降が必要です。IIS側もTLSとHTTP/2を有効にする構成が要り、双方向ストリーミングは使えません。
Blazor WebAssemblyからgRPCサービスを呼べますか?
gRPC-Web経由なら呼べます。Grpc.Net.Client.Webを追加し、GrpcWebHandlerをチャネルに差してください。サーバー側にもgRPC-Webの構成が要ります。ブラウザでサーバーストリーム呼び出しをするならGrpcWebMode.GrpcWebTextを指定します。
C#で書いたサーバをGoやPythonのクライアントから呼べますか?
呼べます。同じprotoから各言語のスタブを生成すればワイヤ上は同じ形式になるためです。実務では相互接続の検証をCIに1本入れておき、TLSの構成が両側で揃っているか、エラー時のステータスコードが期待どおり届くかを確認します。
関連記事
- gRPCとは?仕組み・RESTとの違い・UDP/TCPの扱いを実装例で解説
- Protocol Buffersとは?protoファイルの書き方と採用判断を実装者向けに解説
- gRPC-Webとは?ブラウザからgRPCを呼ぶ仕組みとプロキシ構成・採用判断を解説
- gRPC-Gatewayとは?protoからRESTを生やす仕組みと生成構成・移行判断を解説
- gRPC Goの実装|protoc-gen-go-grpcの生成手順とNewClient移行を解説
- gRPC Pythonの実装|スタブ生成からasync・FastAPI併存まで解説
- gRPC Rustの実装|tonic 0.14のクレート分割とTLS・相互接続を解説