使用 protobuf-net 的 C# 项目中的协议缓冲区 - 代码生成的最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/453820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 03:58:53  来源:igfitidea点击:

Protocol buffers in C# projects using protobuf-net - best practices for code generation

c#visual-studioprotocol-buffersprotobuf-net

提问by Ray Hayes

I'm trying to use protobuf in a C# project, using protobuf-net, and am wondering what is the best way to organise this into a Visual Studio project structure.

我正在尝试在 C# 项目中使用 protobuf,使用 protobuf-net,并且想知道将其组织到 Visual Studio 项目结构中的最佳方法是什么。

When manually using the protogen tool to generate code into C#, life seems easy but it doesn't feel right.

当手动使用 protogen 工具将代码生成到 C# 中时,生活似乎很简单,但感觉不对。

I'd like the .proto file to be considered to be the primary source-code file, generating C# files as a by-product, but before the C# compiler gets involved.

我希望 .proto 文件被视为主要源代码文件,生成 C# 文件作为副产品,但在 C# 编译器参与之前。

The options seem to be:

选项似乎是:

  1. Custom tool for proto tools (although I can't see where to start there)
  2. Pre-build step (calling protogen or a batch-file which does that)
  1. 原型工具的自定义工具(虽然我看不到从哪里开始)
  2. 预构建步骤(调用 protogen 或执行此操作的批处理文件)

I have struggled with 2) above as it keeps giving me "The system cannot find the file specified" unless I use absolute paths (and I don't like forcing projects to be explicitly located).

我一直在努力解决上面的 2) 问题,因为它一直给我“系统找不到指定的文件”,除非我使用绝对路径(并且我不喜欢强制明确定位项目)。

Is there a convention (yet) for this?

对此是否有约定(还)?



Edit:Based upon @jon's comments, I retried the pre-build step method and used this (protogen's location hardcoded for now), using Google's address-book example:

编辑:根据@jon 的评论,我重试了预构建步骤方法并使用了这个(protogen 的位置现在硬编码),使用 Google 的地址簿示例:

c:\bin\protobuf\protogen "-i:$(ProjectDir)AddressBook.proto" 
       "-o:$(ProjectDir)AddressBook.cs" -t:c:\bin\protobuf\csharp.xslt


Edit2:Taking @jon's recommendation to minimise build-time by not processing the .proto files if they haven't changed, I've knocked together a basic tool to check for me (this could probably be expanded to a full Custom-Build tool):

Edit2:采用@jon 的建议,通过不处理 .proto 文件(如果它们没有更改)来最小化构建时间,我已经组合了一个基本工具来检查我(这可能会扩展为完整的自定义构建工具):

using System;
using System.Diagnostics;
using System.IO;

namespace PreBuildChecker
{
    public class Checker
    {
        static int Main(string[] args)
        {
            try
            {
                Check(args);
                return 0;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return 1;
            }
        }

        public static void Check(string[] args)
        {
            if (args.Length < 3)
            {
                throw new ArgumentException(
                    "Command line must be supplied with source, target and command-line [plus options]");
            }

            string source = args[0];
            string target = args[1];
            string executable = args[2];
            string arguments = args.Length > 3 ? GetCommandLine(args) : null;

            FileInfo targetFileInfo = new FileInfo(target);
            FileInfo sourceFileInfo = new FileInfo(source);
            if (!sourceFileInfo.Exists) 
            {
                throw new ArgumentException(string.Format(
                    "Source file {0} not found", source));
            }

            if (!targetFileInfo.Exists || 
                sourceFileInfo.LastWriteTimeUtc > targetFileInfo.LastAccessTimeUtc)
            {
                Process process = new Process();
                process.StartInfo.FileName = executable;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.ErrorDialog = true;

                Console.WriteLine(string.Format(
                     "Source newer than target, launching tool: {0} {1}",
                     executable,
                     arguments));
                process.Start();
            }
        }

        private static string GetCommandLine(string[] args)
        {
            string[] arguments = new string[args.Length - 3];
            Array.Copy(args, 3, arguments, 0, arguments.Length);
            return String.Join(" ", arguments);
        }
    }
}

My pre-build command is now (all on one line):

我的预构建命令现在是(全部在一行上):

$(SolutionDir)PreBuildChecker$(OutDir)PreBuildChecker 
    $(ProjectDir)AddressBook.proto 
    $(ProjectDir)AddressBook.cs 
    c:\bin\protobuf\protogen 
      "-i:$(ProjectDir)AddressBook.proto" 
      "-o:$(ProjectDir)AddressBook.cs" 
      -t:c:\bin\protobuf\csharp.xslt

采纳答案by Marc Gravell

As an extension of Shaun's code, I am pleased to announce that protobuf-net now has Visual Studio integration by way of a Custom Tool. The msi installer is available from the project page. More complete information here: protobuf-net; now with added Orcas.

作为 Shaun 代码的扩展,我很高兴地宣布 protobuf-net 现在通过自定义工具集成了 Visual Studio。可以从项目页面获得 msi 安装程序。更完整的信息在这里:protobuf-net;现在添加了逆戟鲸

Visual Studio with protobuf-net as a Custom Tool

使用 protobuf-net 作为自定义工具的 Visual Studio

回答by Jon Skeet

Calling a pre-build step but using project variables (e.g. $(ProjectPath)) to create absolute filenames without having them actually in your solution would seem a reasonable bet to me.

调用预构建步骤但使用项目变量(例如$(ProjectPath))来创建绝对文件名,而实际上在您的解决方案中没有它们,这对我来说似乎是一个合理的赌注。

One thing you might want to consider, based on my past experience of code generators: you might want to write a wrapper for protogen which generates code to a different location, then checks whether the newly generated code is the same as the old code, and doesn't overwrite it if so. That way Visual Studio will realise nothing's changed and not force that project to be rebuilt - this has cut build times dramaticallyfor me in the past.

根据我过去在代码生成器方面的经验,您可能要考虑的一件事是:您可能想要为 protogen 编写一个包装器,它将代码生成到不同的位置,然后检查新生成的代码是否与旧代码相同,以及如果是这样,不会覆盖它。这样,Visual Studio 将意识到什么都没有改变,并且不会强制重建该项目——这在过去对我来说大大缩短了构建时间。

Alternatively, you could keep an md5 hash of the .proto file the last time protogen was executed, and only execute protogen if the .proto file has changed - even less to do on each build!

或者,您可以保留上次执行 protogen 时 .proto 文件的 md5 哈希值,并且仅在 .proto 文件发生更改时才执行 protogen - 每次构建时都不需要做!

Thanks for raising this as a question though - it clearly suggests I should work out a way to make this an easy pre-build step for my own port.

感谢您提出这个问题 - 它清楚地表明我应该想出一种方法来使这成为我自己的端口的简单预构建步骤。

回答by Andrey Tch.

well, that gave me an idea (something about reinventing the wheel)...

好吧,这给了我一个想法(关于重新发明轮子的东西)......

  • create simple Makefile.mak, something like
  • 创建简单的 Makefile.mak,类似于
.SUFFIXES : .cs .proto

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst

(obviously, don't forget to replace paths to protogen and csharp.xlst). IMPORTANT - protogen\protogen.exe command starting from TAB character, not 8 spaces

(显然,不要忘记替换protogen 和csharp.xlst 的路径)。重要 - protogen\protogen.exe 命令从 TAB 字符开始,而不是 8 个空格

  • If you don't want to specify files needed to be build all the time, you might use something like
  • 如果您不想指定需要一直构建的文件,您可以使用类似
.SUFFIXES : .cs .proto

all: mycs1.cs myotherfile.cs

.proto.cs:
    protogen\protogen.exe -i:$? -o:$@ -t:protogen\csharp.xlst
  • in pre-build step to add
  • 在预构建步骤中添加
cd $(ProjectDir) && "$(DevEnvDir)..\..\vc\bin\nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

or, if you have nmake in your path, one can use

或者,如果您的路径中有 nmake,则可以使用

cd $(ProjectDir) && nmake /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

回答by Andrey Tch.

I have attached a quick and dirty Visual Studio Custom Tool wrapper around ProtoGen.exe to the Google Code page for this issue (http://code.google.com/p/protobuf-net/issues/detail?id=39). This makes adding .proto files to C# projects extremely easy.

我已将 ProtoGen.exe 周围的快速且脏的 Visual Studio 自定义工具包装器附加到此问题的 Google 代码页 ( http://code.google.com/p/protobuf-net/issues/detail?id=39)。这使得将 .proto 文件添加到 C# 项目变得非常容易。

See the readme in the attachment for more info.

有关更多信息,请参阅附件中的自述文件。

回答by Edward Brey

Add the following pre-build event to your project settings to generate the C# file only when the .proto file has changed. Just replace YourFilewith the name of base name of your .proto file.

将以下预构建事件添加到您的项目设置中,以仅在 .proto 文件发生更改时生成 C# 文件。只需替换YourFile为 .proto 文件的基本名称即可。

cd $(ProjectDir) && powershell -Command if (!(Test-Path YourFile.proto.cs) -or (Get-Item YourFile.proto).LastWriteTimeUtc -gt (Get-Item YourFile.proto.cs).LastWriteTimeUtc) { PathToProtoGen\protogen -i:YourFile.proto -o:YourFile.proto.cs }

This works in any recent version of Visual Studio, unlike the protobuf-net Custom-Build tool, which does not support Visual Studio 2012 or Visual Studio 2013, according to issues 338and 413.

这适用于任何最新版本的 Visual Studio,与 protobuf-net 自定义构建工具不同,根据问题338413,它不支持 Visual Studio 2012 或 Visual Studio 2013 。

回答by Tamir Daniely

Add this to the relevant project file.

将此添加到相关的项目文件中。

Advantage, incremental build.

优势,增量构建。

Disadvantage, you need to edit manually when adding files.

缺点,添加文件时需要手动编辑。

<ItemGroup>
    <Proto Include="Person.proto" />
    <Compile Include="Person.cs">
        <DependentUpon>Person.proto</DependentUpon>
    </Compile>
</ItemGroup>
<PropertyGroup>
    <CompileDependsOn>ProtobufGenerate;$(CompileDependsOn)</CompileDependsOn>
</PropertyGroup>
<Target Name="ProtobufGenerate" Inputs="@(Proto)" Outputs="@(Proto->'$(ProjectDir)%(Filename).cs')">
    <ItemGroup>
        <_protoc Include="..\packages\Google.Protobuf.*\tools\protoc.exe" />
    </ItemGroup>
    <Error Condition="!Exists(@(_protoc))" Text="Could not find protoc.exe" />
    <Exec Command="&quot;@(_protoc)&quot; &quot;--csharp_out=$(ProjectDir.TrimEnd('\'))&quot; @(Proto->'%(Identity)',' ')" WorkingDirectory="$(ProjectDir)" />
</Target>