使用 .NET 的代码优先 gRPC 服务和客户端

作者:James Newton-KingMarc Gravell

代码优先 gRPC 使用 .NET 类型来定义服务和消息协定。

当整个系统使用 .NET 时,代码优先是一个不错的选择:

  • 可以在 .NET 服务器和客户端之间共享 .NET 服务和数据协定类型。
  • 无需在 .proto 文件和代码生成过程中定义协定。

不建议在具有多种语言的 polyglot 系统中使用代码优先。 .NET 服务和数据协定类型不能与非 .NET 平台一起使用。 其他平台若要调用使用代码优先编写的 gRPC 服务,必须创建一个与该服务匹配的 .proto 协定。

protobuf-net.Grpc

重要

有关 protobuf-net.Grpc 的帮助信息,请访问 protobuf-net.Grpc 网站或在 protobuf-net.Grpc GitHub 存储库上创建一个问题。

protobuf-net.Grpc 是一个社区项目,不受 Microsoft 支持。 它将对 Grpc.AspNetCoreGrpc.Net.Client 添加代码优先支持。 它使用通过属性批注的 .NET 类型来定义应用的 gRPC 服务和消息。

创建代码优先 gRPC 服务的第一步是定义代码协定:

  • 创建一个将由服务器和客户端共享的新项目。
  • 添加一个 protobuf-net.Grpc 包引用。
  • 创建服务和数据协定类型。
using ProtoBuf.Grpc;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading.Tasks;

namespace Shared.Contracts;

[DataContract]
public class HelloReply
{
    [DataMember(Order = 1)]
    public string Message { get; set; }
}

[DataContract]
public class HelloRequest
{
    [DataMember(Order = 1)]
    public string Name { get; set; }
}

[ServiceContract]
public interface IGreeterService
{
    [OperationContract]
    Task<HelloReply> SayHelloAsync(HelloRequest request,
        CallContext context = default);
}

前面的代码:

  • 定义 HelloRequestHelloReply 消息。
  • 使用一元 SayHelloAsync gRPC 方法定义 IGreeterService 协定接口。

服务协定在服务器上实现并从客户端调用。

在服务接口上定义的方法必须与某些签名匹配,具体取决于它们的流式处理类型:

  • 一元
  • 服务器流式处理
  • 客户端流式处理
  • 双向流式传输

有关定义服务协定的详细信息,请参阅 protobuf-net.Grpc 入门文档

创建代码优先 gRPC 服务

若要将 gRPC 代码优先服务添加到 ASP.NET Core 应用,请执行以下步骤:

  • 添加一个 protobuf-net.Grpc.AspNetCore 包引用。

  • 添加一个对共享代码协定项目的引用。

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
      </ItemGroup>
    
      <ItemGroup>
          <ProjectReference Include="..\Shared\Shared.Contracts.csproj" />
      </ItemGroup>
    
    </Project>
    
  • 创建一个新的 GreeterService.cs 文件并实现 IGreeterService 服务接口:

    using Shared.Contracts;
    using ProtoBuf.Grpc;
    
    public class GreeterService : IGreeterService
    {
        public Task<HelloReply> SayHelloAsync(HelloRequest request, CallContext context = default)
        {
            return Task.FromResult(
                    new HelloReply
                    {
                        Message = $"Hello {request.Name}"
                    });
        }
    }
    
  • 更新 Program.cs 文件:

    using ProtoBuf.Grpc.Server;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Additional configuration is required to successfully run gRPC on macOS.
    // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
    
    // Add services to the container.
    builder.Services.AddCodeFirstGrpc();
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    app.MapGrpcService<GreeterService>();
    app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
    
    app.Run();
    

    前面突出显示的代码更新以下内容:

    • AddCodeFirstGrpc 注册启用了代码优先的服务。
    • MapGrpcService<GreeterService> 添加代码优先的服务终结点。

使用代码优先和 .proto 文件实现的 gRPC 服务可在同一应用中共存。 所有 gRPC 服务都使用 gRPC 服务配置

创建代码优先 gRPC 客户端

代码优先 gRPC 客户端使用服务协定来调用 gRPC 服务。

  • 在 gRPC 客户端 .csproj 文件中:

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Grpc.Net.Client" Version="2.52.0" />
        <PackageReference Include="protobuf-net.Grpc" Version="1.0.152" />
      </ItemGroup>
        
      <ItemGroup>
        <ProjectReference Include="..\Shared\Shared.Contracts.csproj" />
      </ItemGroup>
    
    </Project>
    
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.Net.Client" Version="2.52.0" />
    <PackageReference Include="protobuf-net.Grpc" Version="1.0.152" />
  </ItemGroup>
    
  <ItemGroup>
    <ProjectReference Include="..\Shared\Shared.Contracts.csproj" />
  </ItemGroup>

</Project>
  • 更新客户端 program.cs

    // See https://aka.ms/new-console-template for more information
    using Grpc.Net.Client;
    using ProtoBuf.Grpc.Client;
    using Shared.Contracts;
    
    namespace GrpcGreeterClient;
    
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            using var channel = GrpcChannel.ForAddress("https://localhost:7184");
            var client = channel.CreateGrpcService<IGreeterService>();
    
            var reply = await client.SayHelloAsync(
                new HelloRequest { Name = "GreeterClient" });
    
            Console.WriteLine($"Greeting: {reply.Message}");
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
    
    
    

前面的 gRPC 客户端 Program.cs 代码执行以下操作:

  • 创建一个 gRPC 通道。
  • 使用 CreateGrpcService<IGreeterService> 扩展方法从通道创建代码优先客户端。
  • 使用 SayHelloAsync 调用 gRPC 服务。

代码优先 gRPC 客户端是通过通道创建的。 与常规客户端一样,代码优先客户端使用其通道配置

查看或下载示例代码如何下载

其他资源

代码优先 gRPC 使用 .NET 类型来定义服务和消息协定。

当整个系统使用 .NET 时,代码优先是一个不错的选择:

  • 可以在 .NET 服务器和客户端之间共享 .NET 服务和数据协定类型。
  • 无需在 .proto 文件和代码生成过程中定义协定。

不建议在具有多种语言的 polyglot 系统中使用代码优先。 .NET 服务和数据协定类型不能与非 .NET 平台一起使用。 其他平台若要调用使用代码优先编写的 gRPC 服务,必须创建一个与该服务匹配的 .proto 协定。

protobuf-net.Grpc

重要

有关 protobuf-net.Grpc 的帮助信息,请访问 protobuf-net.Grpc 网站或在 protobuf-net.Grpc GitHub 存储库上创建一个问题。

protobuf-net.Grpc 是一个社区项目,不受 Microsoft 支持。 它将对 Grpc.AspNetCoreGrpc.Net.Client 添加代码优先支持。 它使用通过属性批注的 .NET 类型来定义应用的 gRPC 服务和消息。

创建代码优先 gRPC 服务的第一步是定义代码协定:

  • 创建一个将由服务器和客户端共享的新项目。
  • 添加一个 protobuf-net.Grpc 包引用。
  • 创建服务和数据协定类型。
[DataContract]
public class HelloReply
{
    [DataMember(Order = 1)]
    public string Message { get; set; }
}

[DataContract]
public class HelloRequest
{
    [DataMember(Order = 1)]
    public string Name { get; set; }
}

[ServiceContract]
public interface IGreeterService
{
    [OperationContract]
    Task<HelloReply> SayHelloAsync(HelloRequest request,
        CallContext context = default);
}

前面的代码:

  • 定义 HelloRequestHelloReply 消息。
  • 使用一元 SayHelloAsync gRPC 方法定义 IGreeterService 协定接口。

服务协定在服务器上实现并从客户端调用。 在服务接口上定义的方法必须与某些签名匹配,具体取决于它们是一元、服务器流式处理、客户端流式处理还是双向流式处理。

有关定义服务协定的详细信息,请参阅 protobuf-net.Grpc 入门文档

创建代码优先 gRPC 服务

若要将 gRPC 代码优先服务添加到 ASP.NET Core 应用,请执行以下步骤:

创建一个新的 GreeterService.cs 文件并实现 IGreeterService 服务接口:

public class GreeterService : IGreeterService
{
    public Task<HelloReply> SayHelloAsync(HelloRequest request, CallContext context = default)
    {
        return Task.FromResult(
               new HelloReply
               {
                   Message = $"Hello {request.Name}"
               });
    }
}

更新 Startup.cs 文件:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCodeFirstGrpc();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>();
    });
}

在上述代码中:

  • AddCodeFirstGrpc 注册启用了代码优先的服务。
  • MapGrpcService<GreeterService> 添加代码优先的服务终结点。

使用代码优先和 .proto 文件实现的 gRPC 服务可在同一应用中共存。 所有 gRPC 服务都使用 gRPC 服务配置

创建代码优先 gRPC 客户端

代码优先 gRPC 客户端使用服务协定来调用 gRPC 服务。 若要使用代码优先客户端调用 gRPC 服务,请执行以下步骤:

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = channel.CreateGrpcService<IGreeterService>();

var reply = await client.SayHelloAsync(
    new HelloRequest { Name = "GreeterClient" });

Console.WriteLine($"Greeting: {reply.Message}");

前面的代码:

  • 创建一个 gRPC 通道。
  • 使用 CreateGrpcService<IGreeterService> 扩展方法从通道创建代码优先客户端。
  • 使用 SayHelloAsync 调用 gRPC 服务。

代码优先 gRPC 客户端是通过通道创建的。 与常规客户端一样,代码优先客户端使用其通道配置

查看或下载示例代码如何下载

其他资源