一键导入
service-definition
Use when designing proto files or implementing gRPC service classes with MediatR.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when designing proto files or implementing gRPC service classes with MediatR.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
基于 SOC 职业分类
| name | service-definition |
| description | Use when designing proto files or implementing gRPC service classes with MediatR. |
| metadata | {"category":"microservice/grpc","agent":"command-architect"} |
| when_to_use | When creating or modifying gRPC service definitions or protobuf files |
.proto files)*Base classgoogle.protobuf.StringValue for nullable, google.protobuf.Timestamp for DateTimePageRequest/PageResponse messagessyntax = "proto3";
package {company}.{domain};
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
option csharp_namespace = "{Company}.{Domain}.Grpc";
// Command service
service OrderCommands {
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
rpc UpdateOrder (UpdateOrderRequest) returns (UpdateOrderResponse);
rpc CompleteOrder (CompleteOrderRequest) returns (CompleteOrderResponse);
}
// Query service
service OrderQueries {
rpc GetOrder (GetOrderRequest) returns (OrderResponse);
rpc GetOrders (GetOrdersRequest) returns (GetOrdersResponse);
}
// Messages
message CreateOrderRequest {
string customer_name = 1;
// C-Q3 fix: use minor-unit integers (cents) for money — `double` loses
// precision and is the canonical money anti-pattern. See the anti-pattern
// table below for the recommended choices (int64 cents, string-decimal,
// or google.type.Money / DecimalValue wrapper).
int64 total_cents = 2;
repeated OrderItemMessage items = 3;
}
message CreateOrderResponse {
string order_id = 1;
int32 sequence = 2;
}
message GetOrdersRequest {
int32 page = 1;
int32 page_size = 2;
google.protobuf.StringValue search = 3; // nullable
google.protobuf.StringValue status = 4; // nullable
}
message GetOrdersResponse {
repeated OrderSummary items = 1;
int32 total_count = 2;
int32 page = 3;
int32 page_size = 4;
}
message OrderSummary {
string id = 1;
string customer_name = 2;
int64 total_cents = 3; // C-Q3: minor-unit integer (cents)
string status = 4;
}
message OrderItemMessage {
string product_id = 1;
int32 quantity = 2;
int64 unit_price_cents = 3; // C-Q3: minor-unit integer (cents)
}
namespace {Company}.{Domain}.Grpc.Services;
public sealed class OrderCommandsService(IMediator mediator)
: OrderCommands.OrderCommandsBase
{
public override async Task<CreateOrderResponse> CreateOrder(
CreateOrderRequest request, ServerCallContext context)
{
var command = request.ToCommand();
// C-Q1 fix: forward the RPC's cancellation token so MediatR
// honors client-cancelled / deadline-exceeded RPCs.
var output = await mediator.Send(command, context.CancellationToken);
return output.ToCreateResponse();
}
public override async Task<UpdateOrderResponse> UpdateOrder(
UpdateOrderRequest request, ServerCallContext context)
{
var command = request.ToCommand();
// C-Q1 fix: forward the RPC's cancellation token (see CreateOrder).
var output = await mediator.Send(command, context.CancellationToken);
return output.ToUpdateResponse();
}
}
namespace {Company}.{Domain}.Grpc.Extensions;
public static class OrderMappingExtensions
{
// Request → Command
public static CreateOrderCommand ToCommand(this CreateOrderRequest r) =>
new(r.CustomerName, (decimal)r.Total,
r.Items.Select(i => new OrderItemInput(
Guid.Parse(i.ProductId), i.Quantity, (decimal)i.UnitPrice
)).ToList());
// Output → Response
public static CreateOrderResponse ToCreateResponse(this OrderOutput o) =>
new() { OrderId = o.Id.ToString(), Sequence = o.Sequence };
// Nullable proto → C# nullable
public static string? ToNullable(this StringValue? value) =>
value?.Value;
// DateTime → Timestamp
public static Timestamp ToTimestamp(this DateTime dt) =>
Timestamp.FromDateTime(dt.ToUniversalTime());
}
// Server-side registration
builder.Services.AddGrpc(options =>
{
options.Interceptors.Add<ApplicationExceptionInterceptor>();
options.Interceptors.Add<ThreadCultureInterceptor>();
});
var app = builder.Build();
app.MapGrpcService<OrderCommandsService>();
app.MapGrpcService<OrderQueriesService>();
| Anti-Pattern | Correct Approach |
|---|---|
Using string for nullable fields | Use google.protobuf.StringValue |
| Business logic in gRPC service | Delegate to MediatR handlers |
| Missing mapping extensions | Always have explicit ToCommand/ToResponse |
Using float or double for money | C-Q3: use minor-unit integers (int64 cents), string for fixed-point decimals, or a DecimalValue/google.type.Money wrapper. Never double. |
| Hardcoded strings in service | Use resource strings for error messages |
# Find .proto files
find . -name "*.proto" -type f
# Find gRPC service implementations
grep -r "CommandsBase\|QueriesBase" --include="*.cs" src/
# Find mapping extensions
grep -r "ToCommand\|ToResponse" --include="*.cs" src/Grpc/
# Find MapGrpcService
grep -r "MapGrpcService" --include="*.cs" src/
{domain}-commands.proto, {domain}-queries.protoGrpc/Extensions/ directoryMapGrpcService<T>() in Program.cs.csproj with proto file reference (GrpcServices="Server" or "Both")