当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《使用protobuf-net的C#项目中的协议缓冲区-代码生成的最佳实践》经验,为你挑选了5个好方法。

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

当手动使用protogen工具生成C#代码时,生活似乎很容易,但感觉不对.

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

选项似乎是:

    原型工具的自定义工具(虽然我看不到从哪里开始)

    预构建步骤(调用protogen或批处理文件)

我一直在努力与2)上面,因为它一直给我"系统找不到指定的文件",除非我使用绝对路径(我不喜欢强制项目明确定位).

还有一个约定吗?


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

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

编辑2: 考虑到@ 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);
        }
    }
}

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

$(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

Jon Skeet.. 8

调用预构建步骤但使用项目变量(例如$(ProjectPath))创建绝对文件名而不将它们实际存在于您的解决方案中似乎是合理的选择.

基于我过去的代码生成器经验,你可能想要考虑一件事:你可能想要为protogen编写一个包装器,它将代码生成到不同的位置,然后检查新生成的代码是否与旧代码相同,以及如果是这样,它不会覆盖它.通过这种方式,Visual Studio将不会发生任何变化,也不会强制重建项目 - 这对我来说已经大大缩短了构建时间.

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

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



1> Jon Skeet..:

调用预构建步骤但使用项目变量(例如$(ProjectPath))创建绝对文件名而不将它们实际存在于您的解决方案中似乎是合理的选择.

基于我过去的代码生成器经验,你可能想要考虑一件事:你可能想要为protogen编写一个包装器,它将代码生成到不同的位置,然后检查新生成的代码是否与旧代码相同,以及如果是这样,它不会覆盖它.通过这种方式,Visual Studio将不会发生任何变化,也不会强制重建项目 - 这对我来说已经大大缩短了构建时间.

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

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



2> Marc Gravell..:

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

Visual Studio与protobuf-net作为自定义工具


这似乎现在已被弃用,还有其他工具吗?

3> Edward Brey..:

将以下预构建事件添加到项目设置中,以便仅在.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 }

根据问题338和413,这适用于任何最新版本的Visual Studio,与protobuf-net Custom-Build工具不同,后者不支持Visual Studio 2012或Visual Studio 2013 .



4> Tamir Daniel..:

将其添加到相关的项目文件中.

优势,增量构建.

缺点是,您需要在添加文件时手动编辑.


    
    
        Person.proto
    


    ProtobufGenerate;$(CompileDependsOn)


    
        <_protoc Include="..\packages\Google.Protobuf.*\tools\protoc.exe" />
    
    
    



5> 小智..:

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

创建简单的Makefile.mak,就像

.SUFFIXES : .cs .proto

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

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

如果您不想指定需要一直构建的文件,您可能会使用类似的东西

.SUFFIXES : .cs .proto

all: mycs1.cs myotherfile.cs

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

在预构建步骤中添加

cd $(ProjectDir) && "$(DevEnvDir)..\..\vc\bin\nmake" /NOLOGO -c -f Makefile.mak mycs1.cs myotherfile.cs

或者,如果你在你的道路上有人,你可以使用

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

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有