C# Reference
Type Mappings
| IDL Type | C# Type | Example |
|---|---|---|
string |
string |
"hello" |
int |
long |
42L |
float |
double |
3.14 |
bool |
bool |
true, false |
[]Type |
List<Type> |
new List<int> { 1, 2, 3 } |
map[string]Type |
Dictionary<string, Type> |
new Dictionary<string, Type> |
Enum |
enum |
OrderStatus.Pending |
Struct |
Class with properties | new Product { ... } |
T [optional] |
Nullable<T> or default |
null or default |
Generated Classes
Each struct in your IDL becomes a C# class:
using Checkout;
// Create instances using object initializer
var product = new Product {
ProductId = "prod001",
Name = "Wireless Mouse",
Description = "Ergonomic mouse",
Price = 29.99,
Stock = 50,
ImageUrl = "https://example.com/mouse.jpg" // optional field
};
var cart = new Cart {
CartId = "cart_1234",
Items = new List<CartItem>(),
Subtotal = 0.0
};
Optional Fields
Optional fields can be null:
// Create with optional field
var product = new Product {
ProductId = "prod001",
Name = "Wireless Mouse",
Price = 29.99,
Stock = 50,
ImageUrl = null // optional field can be null
};
// Check optional field
if (product.ImageUrl != null) {
Console.WriteLine(product.ImageUrl);
}
Enums
Enums use proper C# enum with constants:
using Checkout;
// Use enum constants
var order = new Order {
OrderId = "order_123",
Cart = cart,
ShippingAddress = address,
PaymentMethod = PaymentMethod.CreditCard,
Status = OrderStatus.Pending,
Total = 59.98,
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
// Compare enums
if (order.Status == OrderStatus.Pending) {
Console.WriteLine("Order is pending");
}
Error Handling
Throw RPCError with custom codes:
using PulseRPC;
// Standard JSON-RPC errors
throw new RPCError(-32602, "Invalid params");
// Custom application errors (use codes >= 1000)
throw new RPCError(1001, "CartNotFound: Cart does not exist");
throw new RPCError(1002, "CartEmpty: Cannot create order from empty cart");
Common error codes:
-32700: Parse error-32600: Invalid request-32601: Method not found-32602: Invalid params-32603: Internal error1000+: Custom application errors
Server Implementation
Implement generated interfaces:
using PulseRPC;
using Checkout;
class CatalogServiceImpl : ICatalogService {
private List<Product> products = new List<Product> {
new Product { ProductId = "p1", Name = "Item 1", Price = 10.0, Stock = 5 },
new Product { ProductId = "p2", Name = "Item 2", Price = 20.0, Stock = 3 }
};
public List<Product> ListProducts() {
return products;
}
public Product GetProduct(string productId) {
return products.FirstOrDefault(p => p.ProductId == productId);
}
}
// Start server
class Program {
static async Task Main(string[] args) {
var server = new PulseRPCServer();
server.RegisterCatalogService(new CatalogServiceImpl());
await server.RunAsync("localhost", 8080);
}
}
Client Usage
using PulseRPC;
using Checkout;
var transport = new HttpTransport("http://localhost:8080");
var catalog = new CatalogServiceClient(transport);
// Method calls return C# objects
var products = catalog.ListProducts();
foreach (var p in products) {
Console.WriteLine($"{p.Name} - ${p.Price}");
}
// Optional methods return null if not found
var product = catalog.GetProduct("prod001");
if (product != null) {
Console.WriteLine(product.Name);
}
Async/Await Pattern
PulseRPC C# supports async/await:
using System.Threading.Tasks;
class OrderServiceImpl : IOrderService {
public async Task<CheckoutResponse> CreateOrder(CreateOrderRequest request) {
// Async operations
var orderId = await GenerateOrderIdAsync();
var validated = await ValidateCartAsync(request.CartId);
if (!validated) {
throw new RPCException(1002, "CartEmpty: Cannot create order from empty cart");
}
return new CheckoutResponse { OrderId = orderId };
}
}
Validation
PulseRPC automatically validates:
- Required fields are present
- Types match IDL definition
- Enum values are valid
// This will throw RPCException (-32602) if validation fails
var cart = cart.AddToCart(new AddToCartRequest {
CartId = null,
ProductId = "prod001",
Quantity = 2
});
LINQ Integration
Use LINQ for working with collections:
using System.Linq;
// Filter
var inStockProducts = products.Where(p => p.Stock > 0).ToList();
// Project
var productNames = products.Select(p => p.Name).ToList();
// Find
var firstProduct = products.FirstOrDefault(p => p.ProductId == "prod001");
// Aggregate
var totalValue = products.Sum(p => p.Price * p.Stock);
Best Practices
- Use object initializers: Cleaner than constructor parameters
- Null check optionals: Always check for null on optional fields
- Use LINQ: Elegant collection manipulation
- Async for I/O: Use async/await for database/network calls
- Dependency injection: Inject services into constructors
Working with Nested Structs
// Nested structs work naturally
var order = new Order {
OrderId = "order_123",
Cart = new Cart {
CartId = "cart_123",
Items = new List<CartItem> { new CartItem { ... } },
Subtotal = 59.98
},
ShippingAddress = new Address {
Street = "123 Main St",
City = "San Francisco",
State = "CA",
ZipCode = "94105",
Country = "USA"
},
PaymentMethod = PaymentMethod.CreditCard,
Status = OrderStatus.Pending,
Total = 59.98,
CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
.NET Project Integration
Generated code includes .csproj file:
# Build
dotnet build
# Run server
dotnet run --project Server.csproj
# Run client
dotnet run --project Client.csproj
Using with ASP.NET Core
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api")]
public class CheckoutController : ControllerBase {
private readonly ICartService _cartService;
public CheckoutController(ICartService cartService) {
_cartService = cartService;
}
[HttpPost("cart")]
public Cart AddToCart([FromBody] AddToCartRequest request) {
return _cartService.AddToCart(request);
}
}
Dependency Injection
using Microsoft.Extensions.DependencyInjection;
// Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton<ICatalogService, CatalogServiceImpl>();
services.AddSingleton<ICartService, CartServiceImpl>();
services.AddSingleton<IOrderService, OrderServiceImpl>();
}
// Controller
public class CartController : ControllerBase {
private readonly ICartService _cartService;
public CartController(ICartService cartService) {
_cartService = cartService;
}
}
Configuration
Add to appsettings.json:
{
"PulseRPC": {
"ServerUrl": "http://localhost:8080",
"Timeout": 30000
}
}
Load in startup:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var serverUrl = config["PulseRPC:ServerUrl"];
Exception Handling
try {
var response = orders.CreateOrder(request);
Console.WriteLine($"Order created: {response.OrderId}");
}
catch (RPCException ex) {
// Handle application errors
Console.WriteLine($"Error {ex.Code}: {ex.Message}");
}
catch (Exception ex) {
// Handle system errors
Console.WriteLine($"System error: {ex.Message}");
}
Serialization
PulseRPC C# uses System.Text.Json or Newtonsoft.Json:
using System.Text.Json;
// Custom serialization options
var options = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
var json = JsonSerializer.Serialize(product, options);