Isaac.

web

gRPC with ASP.NET Core

Build high-performance services with gRPC.

By Emem IsaacApril 28, 20233 min read
#grpc#aspnet core#performance#protobuf#microservices
Share:

A Simple Analogy

gRPC is like FedEx for APIs. Instead of sending regular letters (REST), you send packages with a guaranteed structure (protobuf), compressed and fast.


Why gRPC?

  • Performance: 10x faster than REST (binary protocol)
  • Streaming: Bidirectional communication
  • Type safety: Auto-generated strong typing
  • Language agnostic: Use from any language
  • Low latency: Multiplexing over HTTP/2

Protocol Buffer Definition

// service.proto
syntax = "proto3";

service OrderService {
  rpc CreateOrder(CreateOrderRequest) returns (OrderResponse);
  rpc GetOrder(GetOrderRequest) returns (Order);
  rpc ListOrders(Empty) returns (stream Order);  // Server streaming
}

message CreateOrderRequest {
  string customer_id = 1;
  repeated LineItem items = 2;
}

message LineItem {
  string product_id = 1;
  int32 quantity = 2;
  float price = 3;
}

message Order {
  string id = 1;
  string customer_id = 2;
  repeated LineItem items = 3;
  float total = 4;
}

message OrderResponse {
  string order_id = 1;
  bool success = 2;
}

message GetOrderRequest {
  string order_id = 1;
}

message Empty {
}

Server Implementation

// Generated from proto file
public class OrderService : OrderService.OrderServiceBase
{
    private readonly IOrderRepository _repository;
    
    public OrderService(IOrderRepository repository)
    {
        _repository = repository;
    }
    
    public override async Task<OrderResponse> CreateOrder(
        CreateOrderRequest request,
        ServerCallContext context)
    {
        try
        {
            var order = new Order
            {
                Id = Guid.NewGuid().ToString(),
                CustomerId = request.CustomerId,
                Items = request.Items.ToList()
            };
            
            await _repository.SaveAsync(order);
            
            return new OrderResponse
            {
                OrderId = order.Id,
                Success = true
            };
        }
        catch (Exception ex)
        {
            throw new RpcException(new Status(
                StatusCode.Internal,
                ex.Message));
        }
    }
    
    public override async Task ListOrders(
        Empty request,
        IServerStreamWriter<Order> responseStream,
        ServerCallContext context)
    {
        var orders = await _repository.GetAllAsync();
        foreach (var order in orders)
        {
            await responseStream.WriteAsync(order);
        }
    }
}

// Register in Program.cs
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<OrderService>();

Client Implementation

// Create channel
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new OrderService.OrderServiceClient(channel);

// Unary call
var response = await client.CreateOrderAsync(
    new CreateOrderRequest
    {
        CustomerId = "cust-123",
        Items =
        {
            new LineItem { ProductId = "p1", Quantity = 2, Price = 29.99f },
            new LineItem { ProductId = "p2", Quantity = 1, Price = 49.99f }
        }
    });

Console.WriteLine($"Created order: {response.OrderId}");

// Server streaming
var call = client.ListOrders(new Empty());
await foreach (var order in call.ResponseStream.ReadAllAsync())
{
    Console.WriteLine($"Order {order.Id}: {order.Total}");
}

Practical Example

public override async Task<Order> GetOrder(
    GetOrderRequest request,
    ServerCallContext context)
{
    // Cancellation support
    if (context.CancellationToken.IsCancellationRequested)
    {
        throw new OperationCanceledException();
    }
    
    var order = await _repository.GetAsync(request.OrderId);
    if (order == null)
    {
        throw new RpcException(new Status(
            StatusCode.NotFound,
            $"Order {request.OrderId} not found"));
    }
    
    return order;
}

Best Practices

  1. Proto versioning: Plan for evolution
  2. Error handling: Use gRPC status codes
  3. Timeouts: Set deadlines for calls
  4. Compression: Enable for large messages
  5. Monitoring: Track performance metrics

Related Concepts

  • GraphQL for flexible queries
  • REST APIs for compatibility
  • HTTP/2 multiplexing
  • Load balancing gRPC

Summary

gRPC provides high-performance, type-safe communication using protocol buffers. Master it for microservices, real-time streaming, and language-agnostic APIs.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours