LinKit.Grpc 1.1.0

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LinKit.Grpc" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LinKit.Grpc" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="LinKit.Grpc" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LinKit.Grpc --version 1.1.0
                    
#r "nuget: LinKit.Grpc, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LinKit.Grpc@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LinKit.Grpc&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=LinKit.Grpc&version=1.1.0
                    
Install as a Cake Tool

LinKit.Grpc

NuGet Version NuGet Downloads

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_case to PascalCase conventions.
  • Seamless CQRS Integration: The generated server code directly uses your local IMediator to execute existing handlers, ensuring your business logic is the single source of truth.
  • Transparent Client Calls: The generated IGrpcMediator client 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 by protoc.
  • "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 by protoc.
  • "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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.1 102 10/31/2026
2.1.0 100 10/30/2026
2.0.0 101 10/24/2026
1.2.0 93 10/22/2026
1.1.0 101 10/20/2026
1.0.5 850 10/6/2025
1.0.4 750 9/27/2025
1.0.3 781 9/26/2025
1.0.2 801 9/26/2025
1.0.1 803 9/26/2025
1.0.0 798 9/5/2025