C# 如何使用gRPC工具生成代码
我已经阅读了教程,我能够生成.cs文件,但它不包括我的任何服务或rpc定义 我已经将C# 如何使用gRPC工具生成代码,c#,protocol-buffers,grpc,C#,Protocol Buffers,Grpc,我已经阅读了教程,我能够生成.cs文件,但它不包括我的任何服务或rpc定义 我已经将protoc添加到我的路径和项目目录中 protoc project1.proto--csharp\u out=“C:\output”--plugin=protoc gen grpc=“C:\Users\me\.nuget\packages\grpc.tools\1.8.0\tools\windows\u x64\grpc\u csharp\u plugin.exe” 控制台中无错误输出您需要添加--grpc\u
protoc
添加到我的路径和项目目录中
protoc project1.proto--csharp\u out=“C:\output”--plugin=protoc gen grpc=“C:\Users\me\.nuget\packages\grpc.tools\1.8.0\tools\windows\u x64\grpc\u csharp\u plugin.exe”
控制台中无错误输出您需要添加
--grpc\u out
命令行选项,例如添加
--grpc_out="C:\output\"
请注意,如果您没有任何服务,它将不会写入任何文件
这里有一个完整的例子。从根目录创建:
- 空的
目录输出
- 带有
和protoc.exe
grpc\u csharp\u plugin.exe的
目录tools
- 带有
的test.proto
目录,如下所示:protos
test.proto
:
syntax = "proto3";
service StackOverflowService {
rpc GetAnswer(Question) returns (Answer);
}
message Question {
string text = 1;
string user = 2;
repeated string tags = 3;
}
message Answer {
string text = 1;
string user = 2;
}
然后运行(全部在一行上;我在这里打破它只是为了可读性):
在
output
目录中,您会发现Test.cs
和TestGrpc.cs对于其他发现这一点的人来说,这里只是一个无用的注释,关于这一点的文档非常过时,完全是错误的
安装Grpc.Tools
不会在软件包文件夹中安装任何东西;这是传统行为,即使在windows上也不再如此
安装Grpc.Tools
时,它将隐藏在本地软件包缓存中,您可以通过调用以下命令查看:
$ dotnet nuget locals all --list
info : http-cache: /Users/doug/.local/share/NuGet/v3-cache
info : global-packages: /Users/doug/.nuget/packages/
info : temp: /var/folders/xx/s2hnzbrj3yn4hp1bg8q9gb_m0000gn/T/NuGetScratch
您需要的二进制文件将位于其中一个文件夹中
最简单的方法是直接从nuget下载Grpc.Tools
包,并在本地安装
我已经破解了这个小助手脚本,它可以在windows/mac/linux上运行,这可能会缓解其他人开始使用它的困难:
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Mono.Unix;
namespace BuildProtocol
{
public class Program
{
private const string ToolsUrl = "https://www.nuget.org/api/v2/package/Grpc.Tools/";
private const string Service = "Greeter";
private static string ProtocolPath = Path.Combine("..", "protos");
private static string Protocol = Path.Combine(ProtocolPath, "helloworld.proto");
private static string Output = Path.Combine("..", "Greeter");
public static void Main(string[] args)
{
RequireTools().Wait();
var protoc = ProtocPath();
var plugin = ProtocPluginPath();
Console.WriteLine($"Using: {protoc}");
Console.WriteLine($"Using: {plugin}");
var command = new string[]
{
$"-I{ProtocolPath}",
$"--csharp_out={Output}",
$"--grpc_out={Output}",
$"--plugin=protoc-gen-grpc=\"{plugin}\"",
Protocol,
};
Console.WriteLine($"Exec: {protoc} {string.Join(' ', command)}");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
FileName = protoc,
Arguments = string.Join(' ', command)
}
};
process.Start();
process.WaitForExit();
Console.WriteLine($"Completed status: {process.ExitCode}");
}
public static async Task RequireTools()
{
if (!Directory.Exists("Tools"))
{
Console.WriteLine("No local tools found, downloading binaries from nuget...");
Directory.CreateDirectory("Tools");
await DownloadTools();
ExtractTools();
}
}
private static void ExtractTools()
{
ZipFile.ExtractToDirectory(Path.Combine("Tools", "tools.zip"), Path.Combine("Tools", "bin"));
}
private static async Task DownloadTools()
{
using (var client = new HttpClient())
{
Console.WriteLine($"Fetching: {ToolsUrl}");
using (var result = await client.GetAsync(ToolsUrl))
{
if (!result.IsSuccessStatusCode) throw new Exception($"Unable to download tools ({result.StatusCode}), check URL");
var localArchive = Path.Combine("Tools", "tools.zip");
Console.WriteLine($"Saving to: {localArchive}");
File.WriteAllBytes(localArchive, await result.Content.ReadAsByteArrayAsync());
}
}
}
private static string ProtocPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "protoc");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static string ProtocPluginPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "grpc_csharp_plugin");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static void RequireExecutablePermission(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
Console.WriteLine($"Ensuring +x on {path}");
var unixFileInfo = new UnixFileInfo(path);
unixFileInfo.FileAccessPermissions = FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite | FileAccessPermissions.UserExecute;
}
private static string WithExeExtensionIfRequired(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path += ".exe";
}
return path;
}
private static string DetermineArch()
{
var arch = RuntimeInformation.OSArchitecture;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return WithArch("windows_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return WithArch("macosx_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return WithArch("linux_", arch);
}
throw new Exception("Unable to determine runtime");
}
private static string WithArch(string platform, Architecture arch)
{
switch (arch)
{
case Architecture.X64:
return $"{platform}x86";
case Architecture.X86:
return $"{platform}x64";
default:
throw new ArgumentOutOfRangeException(nameof(arch), arch, null);
}
}
}
}
@用户3953989:我自己刚试过,没问题。您确定没有合并两个命令行参数吗?您使用的完整命令行是什么?它在OP中。我已将protoc添加到路径中,并从proto所在的目录中运行它,以使其更干净protoc project1.proto--csharp\u out=“C:\output”
工作,但protoc project1.proto--csharp\u out=“C:\output”--plugin=protoc gen grpc=。\grpc\u csharp\u plugin.exe
没有。两者都产生相同的输出,并且没有errors@user3953989:grpc\u csharp\u plugin.exe是否实际位于当前目录中?如果不是,那可能就是问题所在。(但您仍然需要指定--grpc_out)@user3953989:“直接运行”是什么意思?将它放在路径中与放在当前目录中不同。(我这样问是因为将其与您的原始文件放在同一个目录中是很不寻常的。)@user3953989:根据这个答案,您仍然没有指定--grpc_out
。在那一点上它真的会起作用。。。(当然,假设你有一些服务。)顺便说一句,我建议使用更新版本的gRPC。版本1.12现在已经发布了。1.12是我的解决方案中在NuGet manager下显示的——版本显示了3.5.0,但您使用的是1.8.0协议插件。你也应该使用1.12.0版本的。(我不确定您正在运行什么来显示3.5.0,但我建议也使用protoc的3.5.1。)“安装Grpc.Tools不会在软件包文件夹中安装任何东西”-这取决于您如何安装它。如果您使用nuget安装
,那么您完全可以将其放入所需的任何软件包文件夹中。我发现这是访问软件包中文件的一种非常有用的方法。nuget
不是.Net核心SDK的一部分,在其他平台上安装也有问题。安装软件包的标准方法是dotnetaddpackage
,这是大多数ide现在所做的(VS取决于项目类型)。您所说的是正确的,但这并没有改变它是遗留行为的事实(类似于msbuild路径的硬编码版本,而不是使用dotnet msbuild
来查找msbuild的“正确”安装副本)。我认为您前几段的总体语气是不必要的敌意——您还应该记住,可能有许多开发人员没有使用.NET Core SDK,但仍在使用gRPC。我同意如果文件可以改进会更好,但是有更积极的方式来表达这一点。我还想指出的是,很快就会有MSBuilld集成来生成文件,这将使一切变得更容易。@DaisyShipton我不知道该说什么。如果我的语气不合适,我很抱歉,但是教程的说明是错误的,不起作用。如果这一点有所改变,我会很高兴地更新这个答案,但现在,答案是这样的。gRPC C#quickstart似乎已经过时了。我是为了解决这个问题而创建的。它包含使用dotnet SDK时如何执行codegen的更新说明。
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Mono.Unix;
namespace BuildProtocol
{
public class Program
{
private const string ToolsUrl = "https://www.nuget.org/api/v2/package/Grpc.Tools/";
private const string Service = "Greeter";
private static string ProtocolPath = Path.Combine("..", "protos");
private static string Protocol = Path.Combine(ProtocolPath, "helloworld.proto");
private static string Output = Path.Combine("..", "Greeter");
public static void Main(string[] args)
{
RequireTools().Wait();
var protoc = ProtocPath();
var plugin = ProtocPluginPath();
Console.WriteLine($"Using: {protoc}");
Console.WriteLine($"Using: {plugin}");
var command = new string[]
{
$"-I{ProtocolPath}",
$"--csharp_out={Output}",
$"--grpc_out={Output}",
$"--plugin=protoc-gen-grpc=\"{plugin}\"",
Protocol,
};
Console.WriteLine($"Exec: {protoc} {string.Join(' ', command)}");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
FileName = protoc,
Arguments = string.Join(' ', command)
}
};
process.Start();
process.WaitForExit();
Console.WriteLine($"Completed status: {process.ExitCode}");
}
public static async Task RequireTools()
{
if (!Directory.Exists("Tools"))
{
Console.WriteLine("No local tools found, downloading binaries from nuget...");
Directory.CreateDirectory("Tools");
await DownloadTools();
ExtractTools();
}
}
private static void ExtractTools()
{
ZipFile.ExtractToDirectory(Path.Combine("Tools", "tools.zip"), Path.Combine("Tools", "bin"));
}
private static async Task DownloadTools()
{
using (var client = new HttpClient())
{
Console.WriteLine($"Fetching: {ToolsUrl}");
using (var result = await client.GetAsync(ToolsUrl))
{
if (!result.IsSuccessStatusCode) throw new Exception($"Unable to download tools ({result.StatusCode}), check URL");
var localArchive = Path.Combine("Tools", "tools.zip");
Console.WriteLine($"Saving to: {localArchive}");
File.WriteAllBytes(localArchive, await result.Content.ReadAsByteArrayAsync());
}
}
}
private static string ProtocPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "protoc");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static string ProtocPluginPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "grpc_csharp_plugin");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static void RequireExecutablePermission(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
Console.WriteLine($"Ensuring +x on {path}");
var unixFileInfo = new UnixFileInfo(path);
unixFileInfo.FileAccessPermissions = FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite | FileAccessPermissions.UserExecute;
}
private static string WithExeExtensionIfRequired(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path += ".exe";
}
return path;
}
private static string DetermineArch()
{
var arch = RuntimeInformation.OSArchitecture;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return WithArch("windows_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return WithArch("macosx_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return WithArch("linux_", arch);
}
throw new Exception("Unable to determine runtime");
}
private static string WithArch(string platform, Architecture arch)
{
switch (arch)
{
case Architecture.X64:
return $"{platform}x86";
case Architecture.X86:
return $"{platform}x64";
default:
throw new ArgumentOutOfRangeException(nameof(arch), arch, null);
}
}
}
}