作者: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包参考中。你生成的项目文件现在应如下所示:
netstandard2.0
构建它!
此时,你可以使用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代码存储库中提交问题。你的反馈,对于确定构建集成工作的未来发展方向,非常重要!