LinKit.Grpc
1.1.0
See the version list below for details.
dotnet add package LinKit.Grpc --version 1.1.0
NuGet\Install-Package LinKit.Grpc -Version 1.1.0
<PackageReference Include="LinKit.Grpc" Version="1.1.0" />
<PackageVersion Include="LinKit.Grpc" Version="1.1.0" />
<PackageReference Include="LinKit.Grpc" />
paket add LinKit.Grpc --version 1.1.0
#r "nuget: LinKit.Grpc, 1.1.0"
#:package LinKit.Grpc@1.1.0
#addin nuget:?package=LinKit.Grpc&version=1.1.0
#tool nuget:?package=LinKit.Grpc&version=1.1.0
LinKit.Grpc
LinKit.Grpc is a powerful extension for LinKit.Core that bridges the gap between your internal CQRS architecture and the high-performance world of gRPC. It uses source generators to completely automate the creation of gRPC server and client code, eliminating boilerplate, preventing manual mapping errors, and letting you focus purely on your business logic.
🚀 Features
- Zero Boilerplate: No more manually implementing gRPC service classes or writing client call logic. Just decorate your CQRS requests and LinKit does the rest.
- Intelligent Mapping: Automatically maps properties between your Protobuf-generated messages and your C# CQRS DTOs, including nested lists and
snake_casetoPascalCaseconventions. - Seamless CQRS Integration: The generated server code directly uses your local
IMediatorto execute existing handlers, ensuring your business logic is the single source of truth. - Transparent Client Calls: The generated
IGrpcMediatorclient makes remote gRPC calls look exactly like local Mediator calls, simplifying your client-side code. - Robust & Extensible: Built-in support for centralized gRPC channel management, interceptors (for logging, auth, retries), and metadata (header) injection.
- NativeAOT & Trimming Ready: Built from the ground up to be fully compatible with modern .NET deployment targets.
📦 Installation
First, ensure you have LinKit.Core installed. Then, add the gRPC package:
dotnet add package LinKit.Grpc
You will also need the standard gRPC packages for ASP.NET Core:
dotnet add package Grpc.AspNetCore
🖥️ gRPC Server Generation
Turn your CQRS handlers into gRPC services automatically.
Step 1: Define your .proto file
Create your gRPC service contract as you normally would.
Example users.proto:
syntax = "proto3";
option csharp_namespace = "MyApi.GrpcContracts";
package users;
service UserService {
rpc GetUserById (GetUserRequest) returns (GetUserResponse);
}
message GetUserRequest {
int32 user_id = 1;
}
message UserMessage {
int32 id = 1;
string user_name = 2;
}
message GetUserResponse {
UserMessage user = 1;
}
Step 2: Decorate Your CQRS Request
Find the CQRS request that corresponds to your RPC method and decorate it with the [GrpcEndpoint] attribute.
using LinKit.Grpc;
using MyApi.GrpcContracts; // Use the namespace from your .proto
// This attribute links the CQRS query to the gRPC method.
[GrpcEndpoint(typeof(UserService.UserServiceBase), "GetUserById")]
public class GetUserQuery : IQuery<UserDto>
{
// LinKit automatically maps 'user_id' from the proto to this property.
public int UserId { get; set; }
}
// Ensure your C# DTO and handler exist
public class UserDto {
public int Id { get; set; }
public string UserName { get; set; }
}
[CqrsHandler]
public class GetUserHandler : IQueryHandler<GetUserQuery, UserDto>
{
public Task<UserDto> HandleAsync(GetUserQuery query, CancellationToken ct)
=> Task.FromResult(new UserDto { Id = query.UserId, UserName = "Demo User" });
}
typeof(UserService.UserServiceBase)is the base class generated byprotoc."GetUserById"is the exact RPC method name from your service.
Step 3: Register and Map the Service
In your server's Program.cs, register the core CQRS services and then map the generated gRPC service.
var builder = WebApplication.CreateBuilder(args);
// Register standard LinKit CQRS and your handlers
builder.Services.AddLinKitCqrs();
builder.Services.AddGrpc();
var app = builder.Build();
// Map the source-generated gRPC service.
// The name is always "LinKit" + [YourServiceName].
app.MapGrpcService<LinKitUserService>();
app.Run();
That's it! Your GetUserById gRPC endpoint is now live, fully implemented, and ready to serve requests.
💻 gRPC Client Generation
Call your gRPC services with the same clean Mediator API you use for local requests.
Step 1: Decorate the CQRS Request (in your Client Project)
In your client application, reference the shared CQRS request classes and decorate them with the [GrpcClient] attribute.
using LinKit.Grpc;
using MyApi.GrpcContracts;
// This attribute links the CQRS query to the gRPC client method.
[GrpcClient(typeof(UserService.UserClient), "GetUserByIdAsync")]
public class GetUserQuery : IQuery<UserDto>
{
public int UserId { get; set; }
}
typeof(UserService.UserClient)is the client class generated byprotoc."GetUserByIdAsync"is the name of the async client method.
Step 2: Register the gRPC Client Kit
In your client's Program.cs, register the IGrpcMediator and provide the necessary infrastructure for creating channels and interceptors.
var builder = WebApplication.CreateBuilder(args);
// 1. Registers the generated GrpcClientMediator
builder.Services.AddLinKitGrpcClient();
// 2. --- Required: Provide a gRPC channel ---
// This registers a provider that creates and reuses gRPC channels.
builder.Services.AddSingleton<IGrpcChannelProvider, MyChannelProvider>();
// 3. --- Optional: Add cross-cutting concerns ---
builder.Services.AddSingleton<IGrpcInterceptorProvider, MyInterceptorProvider>();
builder.Services.AddScoped<IGrpcMetadataProvider, MyMetadataProvider>();
Step 3: Send Requests via IGrpcMediator
Inject IGrpcMediator and use it. The API is identical to the local IMediator, making the remote call transparent.
public class MyService
{
private readonly IGrpcMediator _grpcMediator;
public MyService(IGrpcMediator grpcMediator)
{
_grpcMediator = grpcMediator;
}
public async Task<UserDto> GetUserFromRemoteApi(int id)
{
var query = new GetUserQuery { UserId = id };
// This looks like a local call, but it's making a gRPC request under the hood!
return await _grpcMediator.QueryAsync(query);
}
}
🔧 Advanced Client Configuration
LinKit provides simple, powerful interfaces to control how gRPC clients are created and behave.
1. Centralized Channel Management (IGrpcChannelProvider)
Best Practice: Do not create GrpcChannel instances on every request. Channels are expensive and should be reused. IGrpcChannelProvider solves this by centralizing channel creation.
The generated client will call GetChannel(Type clientType) to get the appropriate channel for the service it needs to call.
public class MyChannelProvider : IGrpcChannelProvider
{
// Use a dictionary for service discovery or read from configuration
private readonly Dictionary<Type, GrpcChannel> _channels = new();
private readonly IConfiguration _config;
public MyChannelProvider(IConfiguration config)
{
_config = config;
}
public GrpcChannel GetChannel(Type clientType)
{
// Lazy-initialize and cache channels
return _channels.GetOrAdd(clientType, type =>
{
var serviceName = type.Name.Replace("Client", ""); // e.g., "UserService"
var url = _config.GetValue<string>($"GrpcEndpoints:{serviceName}");
if (string.IsNullOrEmpty(url))
{
throw new InvalidOperationException($"No gRPC URL configured for {serviceName}");
}
return GrpcChannel.ForAddress(url);
});
}
}
2. Applying Interceptors (IGrpcInterceptorProvider)
Interceptors are the gRPC equivalent of middleware. Use them for logging, authentication, retries, etc.
The provider returns an array of interceptors for a given client type, allowing you to apply different interceptors to different services.
public class MyInterceptorProvider : IGrpcInterceptorProvider
{
private readonly LoggingInterceptor _loggingInterceptor;
private readonly AuthInterceptor _authInterceptor;
public MyInterceptorProvider(ILoggerFactory loggerFactory, ITokenService tokenService)
{
_loggingInterceptor = new LoggingInterceptor(loggerFactory);
_authInterceptor = new AuthInterceptor(tokenService);
}
public Interceptor[] GetInterceptors(Type clientType)
{
// Apply logging to all services
if (clientType == typeof(UserService.UserClient))
{
// Apply auth only to the user service
return new Interceptor[] { _loggingInterceptor, _authInterceptor };
}
return new Interceptor[] { _loggingInterceptor };
}
}
LinKit correctly uses invoker.Intercept(interceptors) to apply your interceptors for optimal performance.
3. Injecting Metadata/Headers (IGrpcMetadataProvider)
Use this to add request headers, such as authentication tokens or tracing IDs. This provider is often registered with a Scoped lifetime.
public class MyMetadataProvider : IGrpcMetadataProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyMetadataProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Metadata GetMetadata()
{
var headers = new Metadata();
// Forward the authorization header from the incoming HTTP request
var authHeader = _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader))
{
headers.Add("Authorization", authHeader);
}
return headers;
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Grpc.AspNetCore (>= 2.71.0)
- Grpc.Core (>= 2.46.6)
- LinKit.Core (>= 2.4.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
-
net8.0
- Grpc.AspNetCore (>= 2.71.0)
- Grpc.Core (>= 2.46.6)
- LinKit.Core (>= 2.4.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
-
net9.0
- Grpc.AspNetCore (>= 2.71.0)
- Grpc.Core (>= 2.46.6)
- LinKit.Core (>= 2.4.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.