بنقرة واحدة
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 ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
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.
| 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")