Importing .proto files from another project

12,540

Solution 1

You can do so by adding the ProtoRoot attribute to the <Protobuf /> section in your .csproj file.

Let's say you have a .proto file in project A:

syntax = "proto3";
option csharp_namespace = "Project.A";
import "ProjectB/<path>/Engine.proto"

message Car {
    Engine engine = 1;
    ...
}

In project B you have:

syntax = "proto3";
option csharp_namespace = "Project.B";

message Engine {
    ...
}

As you can see, in car.proto we used an import statement to a .proto file from another project. In order to successfully import this file, we need to add ProtoRoot to the <Protobuf /> section in the .csproj file of the project A:

<ItemGroup>
  <Protobuf Include="ProjectA/<path>/car.proto" Link="<path>/car.proto" ProtoRoot=".." />
</ItemGroup>

<path> is equivalent to your folder structure within your .NET project. ProtoRoot needs to be set to the directory where import declarations in the .proto files are looking for files. In this case, it's the parent folder which contains two subfolders with project A and project B.

More interesting stuff can be found here: https://chromium.googlesource.com/external/github.com/grpc/grpc/+/HEAD/src/csharp/BUILD-INTEGRATION.md

Solution 2

This is quite more complicated than I've foreseen due the amounts of "moving parts" plus the documentation is somehow outdated and at the moment of writing some attributes appears to behave differently from the post above.

Basically in the car example above, using Visual Studio 2022 you should end up with this enter image description here

to obtain this, ProjA needs a quite of work, the interesting bits appers to be

<ItemGroup>
   <ProjectReference Include="../ProjB/ProjB.csproj" />
</ItemGroup>

<ItemGroup> 
   <Protobuf Include="..\ProjB\Protos\engine.proto" GrpcServices="None" ProtoCompile="False">
      <Link>ProjB\Protos\engine.proto</Link>      
   </Protobuf>  
   <Protobuf Include="../ProjA/Protos/car.proto" Link="Protos/car.Proto" GrpcServices="None" ProtoRoot=".." />
</ItemGroup>

Please note that the important part is, as Yury Yatskov and Ivan Ivković said, to set the ProtoRoot=".." but it is mandatory that the path specified in ProtoRoot is a prefix of the path in the Include attribute. Note:

  • ProtoRoot is relative to the .csproj file
  • Import is relative to the .csproj file BUT need to have ProtoRoot as prefix, so basically one level up and then again enter inside the ProjA again

this way the include statement in car.proto will be able to start from the path specified in ProtoRoot for searching for the .proto files to include

** ProjA/Protos/car.proto

syntax = "proto3";

import "ProjB/Protos/Engine.proto";

option csharp_namespace = "ProjA";

package ProjA;

message Car{
    ProjB.Engine engine = 1;
    string licensePlate = 2;
}

** ProjB/Protos/engine.proto

syntax = "proto3";
option csharp_namespace = "ProjB";

package ProjB;

message Engine{
    string name = 1;
}

Again note that of course you will need to reference Engine message using its package so ProjB.Engine

Finally, this allows to include the ProjB as a normal reference of the ProjA

enter image description here

but PAY ATTENTION to set Compile Protobuf option to No (as you can see in the .csproj reported above) otherwise there will be class name conflicts as both projects will redefine the same "Engine" class.

grpc stub classes attributes seems to not play a big role here, it is more interesting if the .proto contains a service definition, I think.

Solution 3

Another option. It's easier not to use a shared project. The .proto file needs to be placed only in the service project and specified

<ItemGroup>
    <Protobuf Include="Protos\address.proto" GrpcServices="Server" />
</ItemGroup>

and in the project the client only needs to specify the link like this

<ItemGroup>
    <Protobuf Include="..\NameServerProject\Protos\address.proto" GrpcServices="Client">
        <Link>Protos\address.proto</Link>
    </Protobuf>
</ItemGroup>

ProtoRoot can be missed.

Share:
12,540
Joshlo
Author by

Joshlo

Updated on June 04, 2022

Comments

  • Joshlo
    Joshlo almost 2 years

    I have several contract projects that contains different protobuf files, but some of the message types have the same message type like

    message user
    {
      Address address = 1
    }
    
    message Address 
    {
      ....
    }
    

    I have now created a shared project and added an Address.proto file to it only containing

    syntax = "proto3"
    option csharp_namespace = "shared.protos"
    package AddressPackage
    message Address {....}
    

    My problem is to figure out how to import it into the protos in my different contract projects. I have added the shared project as a reference, but everything else that I have tried from there has resultet in errors.

    I know that I need to use import just haven't figured out how to write the string.

    Update

    I'm using gRPC.tools nuget and all .proto files is set to protobuf compiler both

    The files structure is as following

    User.Contracts project

    • Protos -- User.proto Shared project
    • Protos -- Address.proto

    both projects is in it's own folder and those folders are placed next to each other.

    in the shared project it says

    <ItemGroup>
      <None Remove="Protos\Address.proto" />
    </ItemGroup>
    
    <ItemGroup>
      <Protobuf Include="Protos\Address.proto">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Protobuf>
    </ItemGroup>
    

    and in the user.contract is says

    <ItemGroup>
      <None Remove="Protos\User.proto" />
    </ItemGroup>
    
    <ItemGroup>
      <Protobuf Include="Protos\User.proto" />
    </ItemGroup>
    

    Thanks in advance.

    • Marc Gravell
      Marc Gravell over 4 years
      Are you using gRPC.tools for this? If so: what does the relevant csproj pieces look like for the .proto files? Or are you using protoc directly? Or ..? And: what is the folder structure of the various files, relative to the project root?
    • Joshlo
      Joshlo over 4 years
      @MarcGravell please see the update
    • Marc Gravell
      Marc Gravell over 4 years
      It sounds like you're looking for the ProtoRoot="whatever" optional argument on <Protobuf>, which is similar to the include arg on protoc - try <Protobuf ProtoRoot="Protos" ... > ?
  • Matthew Heimlich
    Matthew Heimlich about 3 years
    Getting a file not found error when doing this method. What should the import look like in the proto file you're attempting to add the linked file to? Simply doing 'import "address.proto"; or 'import "Protos\address.proto"' as I would expect in this case seems to not work. The link is made correctly in my IDE to the file in question, and I can reference it in my C# code, but seemingly not from my .proto file.
  • Yury Yatskov
    Yury Yatskov about 3 years
    In the service project, you need to add a proto file, then add the Protobuf element to the ItemGroup. Then you need to build the project. After that, in the client's project, you need to add the Protobuf element to the ItemGroup and build the project. You also need to make sure that the path to the folder with the file is specified correctly. I use IDE VisualStudio 2019 last.
  • John Demetriou
    John Demetriou almost 3 years
    The class is created, but I cannot import it in my proto files, compiler says file not found
  • John Demetriou
    John Demetriou almost 3 years
    This didn't help me. I see the proto from Project B as a link inside Project A, but when importing in a proto file in Project A, I get file not found error.
  • Diogo
    Diogo almost 3 years
    how would this look like when importing protos from nuget?
  • Yury Yatskov
    Yury Yatskov almost 3 years
    John Demetriou, if you have a problem, please show your projects.
  • John Demetriou
    John Demetriou almost 3 years
    I decided to use nuget packages instead. I put my protos in a nuget package, and whoever wants them, references the nuget package
  • Yury Yatskov
    Yury Yatskov almost 3 years
    This is a good approach to put into a nuget package. There is also some good material on gRPC and .proto from Mehmet Özkaya. medium.com/aspnetrun/… github.com/aspnetrun/run-aspnet-grpc