gRPC遇见.NET SDK和Visual Studio:构建时自动生成编码

作者: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代码存储库中提交问题。你的反馈,对于确定构建集成工作的未来发展方向,非常重要!

相关推荐