跳到主要内容

使用 entproto 生成 Protobufs

由于 Ent 和 Protobuf 的模式并不相同,我们必须在模式上提供注解来帮助 entproto 准确识别如何生成 Protobuf 定义(在 protobuf 术语中成为 “消息 Message”)。

我们要做的第一件事情是添加 entproto.Message() 注解。 这是我们对 Protobuf 模式生成的选择性支持,我们无需为 所有 模式实体生成 proto 消息或 gRPC 服务定义, 该注解让我们可以控制这一点。将其追加到 ent/schema/user.go

ent/schema/user.go
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
}
}

下一步我们需要注解每个字段并为其分配字段编号。 请记住当 定义 protobuf 消息类型 时每个字段都必须分配唯一编号。为做到这一点我们为每个字段添加 entproto.Field 注解。在 ent/schema/user.go 中更新 Fields

ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Unique().
Annotations(
entproto.Field(2),
),
field.String("email_address").
Unique().
Annotations(
entproto.Field(3),
),
}
}

请注意我们的字段编号不是从 1 开始,因为 ent 为实体隐式地创建 ID 字段,且字段自动被分配编号 1。 我们现在可以生成 protobuf 消息类型定义了。 向 ent/generate.go 直接添加 go:generate 会触发 entproto 命令行工具。如下:

ent/generate.go
package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
//go:generate go run -mod=mod entgo.io/contrib/entproto/cmd/entproto -path ./schema

让我们重新生成代码:

go generate ./...

可以看到一个名为 ent/proto 的包含所有 protobuf 关联的生成代码的新目录已经被创建了。现在看包括:

ent/proto
└── entpb
├── entpb.proto
└── generate.go

有两个文件被创建了,让我们看一下他们的内容:

ent/proto/entpb/entpb.proto
// Code generated by entproto. DO NOT EDIT.
syntax = "proto3";

package entpb;

option go_package = "ent-grpc-example/ent/proto/entpb";

message User {
int32 id = 1;

string user_name = 2;

string email_address = 3;
}

很好!一个包含消息类型定义的映射到 User 模式的新的.proto 文件已经被创建了!

ent/proto/entpb/generate.go
package entpb
//go:generate protoc -I=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --entgrpc_out=.. --entgrpc_opt=paths=source_relative,schema_path=../../schema entpb/entpb.proto

一个可以唤起 protoc 的新的 generate.go 文件已经被创建了,protobuf 代码生成器会告诉它如何从 .proto 文件生成 Go 语言代码。 为使这个命令能够运行,我们必须首先安装 protoc 及其三个插件:protoc-gen-go(用来生成 GO 语言 Protobuf 结构体)、 protoc-gen-go-grpc(生成 Go 语言 gRPC 服务接口和客户端)、protoc-gen-entgrpc(生成服务接口的实现)。 如果你没有安装这些,请直接参考如下内容:

安装完这些依赖后再次运行代码生成:

go generate ./...

可以看到一个包含为实体而生成 GO 语言结构体的 ent/proto/entpb/entpb.pb.go 文件已经被创建了。

让我们用它写一个测试确保一切都正确连接。创建名为 pb_test.go 的新文件并写入:

package main

import (
"testing"

"ent-grpc-example/ent/proto/entpb"
)

func TestUserProto(t *testing.T) {
user := entpb.User{
Name: "rotemtam",
EmailAddress: "rotemtam@example.com",
}
if user.GetName() != "rotemtam" {
t.Fatal("expected user name to be rotemtam")
}
if user.GetEmailAddress() != "rotemtam@example.com" {
t.Fatal("expected email address to be rotemtam@example.com")
}
}

运行它:

go get -u ./... # install deps of the generated package
go test ./...

哇!测试通过了。我们成功由 Ent 模式生成了可以工作的 Go 语言 Protobuf 结构体。 下一步我们看一下如何由模式自动生成可以工作的 CRUD gRPC 服务器