gRPC遇見.NET SDK和Visual Studio:構建時自動生成編碼

Donald發表於2018-12-20

作者:Kirill`kkm`Katsnelson

作為微軟向其跨平臺.NET產品發展的一部分,他們大大簡化了專案檔案格式,並允許第三方程式碼生成器與.NET專案的緊密整合。我們一直傾聽,現在很自豪地介紹從Grpc.Tools NuGet包的1.17版本開始,.NET C#專案中的Protocol Buffer和gRPC服務.proto檔案的整合編譯。1.17版本現在可以從Nuget.org獲得。

你不再需要使用手寫指令碼從.proto檔案生成程式碼:.NET構建神奇地為你處理此問題。整合工具在呼叫程式碼生成器之前,定位proto編譯器和gRPC外掛,標準Protocol Buffer匯入和跟蹤依賴關係,以便生成的C#原始檔永遠不會過時,同時將重新生成保持在最低要求。實質上,.proto檔案被視為.NET C#專案中的第一類源。

演練

在這篇博文中,我們將介紹最簡單,且可能是最常見的方案,使用跨平臺dotnet命令從.proto檔案建立庫。我們將基本實現Greeter庫的克隆,由C#Helloworld示例目錄中的客戶端和伺服器專案共享。

建立新專案

讓我們從建立新的庫專案開始。

~/work$ dotnet new classlib -o MyGreeter
The template "Class library" was created successfully.

~/work$ cd MyGreeter
~/work/MyGreeter$ ls -lF
total 12
-rw-rw-r-- 1 kkm kkm   86 Nov  9 16:10 Class1.cs
-rw-rw-r-- 1 kkm kkm  145 Nov  9 16:10 MyGreeter.csproj
drwxrwxr-x 2 kkm kkm 4096 Nov  9 16:10 obj/

觀察到dotnet new命令建立了我們不需要的檔案Class1.cs,因此將其刪除。另外,我們需要一些.proto檔案來編譯。在本練習中,我們將從gRPC發行版中複製示例檔案examples/protos/helloworld.proto

~/work/MyGreeter$ rm Class1.cs
~/work/MyGreeter$ wget -q https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto

(在Windows上,使用del Class1.cs,如果你沒有wget命令,只需開啟上面的URL,並使用Web瀏覽器中的“另存為…”命令)。

接下來,將必需的NuGet包新增到專案中:

~/work/MyGreeter$ dotnet add package Grpc
info : PackageReference for package `Grpc` version `1.17.0` added to file `/home/kkm/work/MyGreeter/MyGreeter.csproj`.
~/work/MyGreeter$ dotnet add package Grpc.Tools
info : PackageReference for package `Grpc.Tools` version `1.17.0` added to file `/home/kkm/work/MyGreeter/MyGreeter.csproj`.
~/work/MyGreeter$ dotnet add package Google.Protobuf
info : PackageReference for package `Google.Protobuf` version `3.6.1` added to file `/home/kkm/work/MyGreeter/MyGreeter.csproj`.

將.proto檔案新增到專案中

接下來是一個重要的部分。首先,預設情況下,.csproj專案檔案會自動在其目錄中找到所有.cs檔案,儘管Microsoft現在建議禁止這種通配行為,所以我們也決定不通配.proto檔案。因此,必須明確地將.proto檔案新增到專案中。

其次,將屬性PrivateAssets=“All”新增到Grpc.Tools包參考中是非常重要,這樣新庫的使用者就不會不必要地獲取它。這是有道理的,因為程式包只包含編譯器、程式碼生成器和匯入檔案,這些在.proto檔案編譯的專案之外是不需要的。雖然,在這個簡單的演練中並非嚴格要求,但始終應該是你的標準做法。

因此,編輯檔案MyGreeter.csproj以新增helloworld.proto以便將其編譯,並將PrivateAssets屬性新增到Grpc.Tools包參考中。你生成的專案檔案現在應如下所示:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.6.1" />
    <PackageReference Include="Grpc" Version="1.17.0" />

    <!-- The Grpc.Tools package generates C# sources from .proto files during
         project build, but is not needed by projects using the built library.
         It`s IMPORTANT to add the `PrivateAssets="All"` to this reference: -->
    <PackageReference Include="Grpc.Tools" Version="1.17.0" PrivateAssets="All" />

    <!-- Explicitly include our helloworld.proto file by adding this line: -->
    <Protobuf Include="helloworld.proto" />
  </ItemGroup>

</Project>

構建它!

此時,你可以使用dotnet build命令構建專案,以編譯.proto檔案和庫程式集。在本演練中,我們將在命令中新增日誌切換開關-v:n,所以我們可以看到編譯helloworld.proto檔案的命令是在執行。你可能會發現,在第一次編譯專案時,總是這樣做是個好主意!

請注意,下面省略了許多輸出行,因為構建輸出非常詳細。

~/work/MyGreeter$ dotnet build -v:n

Build started 11/9/18 5:33:44 PM.
  1:7>Project "/home/kkm/work/MyGreeter/MyGreeter.csproj" on node 1 (Build target(s)).
   1>_Protobuf_CoreCompile:
      /home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/protoc
        --csharp_out=obj/Debug/netstandard2.0
        --plugin=protoc-gen-grpc=/home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/grpc_csharp_plugin
        --grpc_out=obj/Debug/netstandard2.0 --proto_path=/home/kkm/.nuget/packages/grpc.tools/1.17.0/build/native/include
        --proto_path=. --dependency_out=obj/Debug/netstandard2.0/da39a3ee5e6b4b0d_helloworld.protodep helloworld.proto
     CoreCompile:

        [ ... skipping long output ... ]

       MyGreeter -> /home/kkm/work/MyGreeter/bin/Debug/netstandard2.0/MyGreeter.dll

Build succeeded.

如果此時再次呼叫dotnet build -v:n命令,則不會呼叫protoc,也不會編譯C#源。但是,如果你更改了helloworld.proto原始碼,那麼在構建期間它的輸出將被重新生成,然後由C#編譯器重新編譯。這是你期望修改任何原始檔的常規依賴關係跟蹤行為。

當然,你也可以將.cs檔案新增到同一個專案中:畢竟,它是構建.NET庫的常規C#專案。我們在RouteGuide示例中是這樣做的。

生成的檔案在哪裡?

你可能想知道原型編譯器和gRPC外掛輸出C#檔案的位置。預設情況下,它們與其他生成的檔案,放在同一目錄中,例如物件(在.NET構建用語中稱為“中間輸出”目錄),在obj/目錄下。這是.NET構建的常規做法,因此自動生成的檔案,不會使工作目錄混亂,或意外地置於原始碼控制之下。否則,偵錯程式等工具可以訪問它們。你也可以在該目錄中看到其他自動生成的源:

~/work/MyGreeter$ find obj -name `*.cs`
obj/Debug/netstandard2.0/MyGreeter.AssemblyInfo.cs
obj/Debug/netstandard2.0/Helloworld.cs
obj/Debug/netstandard2.0/HelloworldGrpc.cs

(如果你從Windows命令提示符下執行此演練,請使用dir /s obj* .cs)

還有更多

雖然,在許多情況下最簡單的預設行為是足夠的,但是有很多方法可以在大型專案中,微調.proto編譯過程。如果你發現預設安排不適合你的工作流程,我們建議你閱讀文件檔案BUILD-INTEGRATION.md,以獲取可用選項。該軟體包還擴充套件了Visual Studio的“屬性”視窗,因此你可以在Visual Studio介面中為每個檔案設定一些選項。

“經典”.csproj專案和Mono也有支援。

分享你的經驗

與任何複雜功能的初始版本一樣,我們很高興收到你的反饋。有什麼不符合預期的工作?你有不容易用新工具覆蓋的場景嗎?你是否知道如何改善工作流程?請仔細閱讀文件,然後在GitHub上的gRPC程式碼儲存庫中提交問題。你的反饋,對於確定構建整合工作的未來發展方向,非常重要!

相關文章