--- title: "gRPC Meets .NET SDK And Visual Studio: Automatic Codegen On Build" author: Kirill 'kkm' Katsnelson author-link: https://github.com/kkm000 date: 2018-12-18 --- As part of Microsoft's move towards its cross-platform .NET offering, they have greatly simplified the project file format, and allowed a tight integration of third-party code generators with .NET projects. We are listening, and now proud to introduce integrated compilation of Protocol Buffer and gRPC service `.proto` files in .NET C# projects starting with the version 1.17 of the Grpc.Tools NuGet package, now available from Nuget.org. You no longer need to use hand-written scripts to generate code from `.proto` files: The .NET build magic handles this for you. The integrated tools locate the proto compiler and gRPC plugin, standard Protocol Buffer imports, and track dependencies before invoking the code generators, so that the generated C# source files are never out of date, at the same time keeping regeneration to the minimum required. In essence, `.proto` files are treated as first-class sources in a .NET C# project. ## A Walkthrough In this blog post, we'll walk through the simplest and probably the most common scenario of creating a library from `.proto` files using the cross-platform `dotnet` command. We will implement essentially a clone of the `Greeter` library, shared by client and server projects in the [C# `Helloworld` example directory ](https://github.com/grpc/grpc/tree/master/examples/csharp/Helloworld/Greeter). ### Create a new project Let's start by creating a new library project. ```sh ~/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/ ``` Observe that the `dotnet new` command has created the file `Class1.cs` that we won't need, so remove it. Also, we need some `.proto` files to compile. For this exercise, we'll copy an example file [`examples/protos/helloworld.proto` ](https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto) from the gRPC distribution. ```sh ~/work/MyGreeter$ rm Class1.cs ~/work/MyGreeter$ wget -q https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto ``` (on Windows, use `del Class1.cs`, and, if you do not have the wget command, just [open the above URL ](https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto) and use a *Save As...* command from your Web browser). Next, add required NuGet packages to the project: ```sh ~/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'. ``` ### Add `.proto` files to the project **Next comes an important part.** First of all, by default, a `.csproj` project file automatically finds all `.cs` files in its directory, although [Microsoft now recommends suppressing this globbing behavior](https://docs.microsoft.com/dotnet/core/tools/csproj#recommendation), so we too decided against globbing `.proto` files. Thus the `.proto` files must be added to the project explicitly. Second of all, it is important to add a property `PrivateAssets="All"` to the Grpc.Tools package reference, so that it will not be needlessly fetched by the consumers of your new library. This makes sense, as the package only contains compilers, code generators and import files, which are not needed outside of the project where the `.proto` files have been compiled. While not strictly required in this simple walkthrough, it must be your standard practice to do that always. So edit the file `MyGreeter.csproj` to add the `helloworld.proto` so that it will be compiled, and the `PrivateAssets` property to the Grpc.Tools package reference. Your resulting project file should now look like this: ```xml netstandard2.0 ``` ### Build it! At this point you can build the project with the `dotnet build` command to compile the `.proto` file and the library assembly. For this walkthrough, we'll add a logging switch `-v:n` to the command, so we can see that the command to compile the `helloworld.proto` file was in fact run. You may find it a good idea to always do that the very first time you compile a project! Note that many output lines are omitted below, as the build output is quite verbose. ```sh ~/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. ``` If at this point you invoke the `dotnet build -v:n` command again, `protoc` would not be invoked, and no C# sources would be compiled. But if you change the `helloworld.proto` source, then its outputs will be regenerated and then recompiled by the C# compiler during the build. This is a regular dependency tracking behavior that you expect from modifying any source file. Of course, you can also add `.cs` files to the same project: It is a regular C# project building a .NET library, after all. This is done in our [RouteGuide ](https://github.com/grpc/grpc/tree/master/examples/csharp/RouteGuide/RouteGuide) example. ### Where are the generated files? You may wonder where the proto compiler and gRPC plugin output C# files are. By default, they are placed in the same directory as other generated files, such as objects (termed the "intermediate output" directory in the .NET build parlance), under the `obj/` directory. This is a regular practice of .NET builds, so that autogenerated files do not clutter the working directory or accidentally placed under source control. Otherwise, they are accessible to the tools like the debugger. You can see other autogenerated sources in that directory, too: ```sh ~/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 ``` (use `dir /s obj\*.cs` if you are following this walkthrough from a Windows command prompt). ## There Is More To It While the simplest default behavior is adequate in many cases, there are many ways to fine-tune your `.proto` compilation process in a large project. We encourage you to read the [documentation file BUILD-INTEGRATION.md ](https://github.com/grpc/grpc/blob/master/src/csharp/BUILD-INTEGRATION.md) for available options if you find that the default arrangement does not suit your workflow. The package also extends the Visual Studio's Properties window, so you may set some options per file in the Visual Studio interface. "Classic" `.csproj` projects and Mono are also supported. ## Share Your Experience As with any initial release of a complex feature, we are thrilled to receive your feedback. Did something not work as expected? Do you have a scenario that is not easy to cover with the new tools? Do you have an idea how to improve the workflow in general? Please read the documentation carefully, and then [open an issue](https://github.com/grpc/grpc/issues) in the gRPC code repository on GitHub. Your feedback is important to determine the future direction for our build integration work!