SkillAgentSearch skills...

ServiceModel.Grpc

Code-first for gRPC

Install / Use

/learn @max-ieremenko/ServiceModel.Grpc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ServiceModel.Grpc

ServiceModel.Grpc enables applications to communicate with gRPC services using a code-first approach (no .proto files), helps to get around limitations of gRPC protocol like "only reference types", "exact one input", "no nulls", "no value-types". Provides exception handling. Helps to migrate existing WCF solution to gRPC with minimum effort.

The library supports lightweight runtime proxy generation via Reflection.Emit and C# source code generation.

The solution is built on top of gRPC C# and grpc-dotnet.

Links

ServiceModel.Grpc at a glance <a name="intro"></a>

Declare a service contract

[ServiceContract]
public interface ICalculator
{
    [OperationContract]
    Task<long> Sum(long x, int y, int z, CancellationToken token = default);

    [OperationContract]
    ValueTask<(int Multiplier, IAsyncEnumerable<int> Values)> MultiplyBy(IAsyncEnumerable<int> values, int multiplier, CancellationToken token = default);
}

Client call (Reflection.Emit)

A proxy for the ICalculator service will be generated on demand via Reflection.Emit.

PS> Install-Package ServiceModel.Grpc
// create a channel
var channel = new Channel("localhost", 5000, ...);

// create a client factory
var clientFactory = new ClientFactory();

// request the factory to generate a proxy for ICalculator service
var calculator = clientFactory.CreateClient<ICalculator>(channel);

// call Sum: sum == 6
var sum = await calculator.Sum(1, 2, 3);

// call MultiplyBy: multiplier == 2, values == [] {2, 4, 6}
var (multiplier, values) = await calculator.MultiplyBy(new[] {1, 2, 3}, 2);

Client call (source code generation)

A proxy for the ICalculator service will be generated in the source code.

PS> Install-Package ServiceModel.Grpc.DesignTime
// request ServiceModel.Grpc to generate a source code for ICalculator service proxy
[ImportGrpcService(typeof(ICalculator))]
internal static partial class MyGrpcServices
{
    // generated code ...
    public static IClientFactory AddCalculatorClient(this IClientFactory clientFactory, Action<ServiceModelGrpcClientOptions> configure = null) {}
}

// create a channel
var channel = new Channel("localhost", 5000, ...);

// create a client factory
var clientFactory = new ClientFactory();

// register ICalculator proxy generated by ServiceModel.Grpc.DesignTime
clientFactory.AddCalculatorClient();

// create a new instance of the proxy
var calculator = clientFactory.CreateClient<ICalculator>(channel);

// call Sum: sum == 6
var sum = await calculator.Sum(1, 2, 3);

// call MultiplyBy: multiplier == 2, values == [] {2, 4, 6}
var (multiplier, values) = await calculator.MultiplyBy(new[] {1, 2, 3}, 2);

Implement a service

internal sealed class Calculator : ICalculator
{
    public Task<long> Sum(long x, int y, int z, CancellationToken token) => x + y + z;

    public ValueTask<(int Multiplier, IAsyncEnumerable<int> Values)> MultiplyBy(IAsyncEnumerable<int> values, int multiplier, CancellationToken token)
    {
        var multiplicationResult = DoMultiplication(values, multiplier, token);
        return new ValueTask<(int, IAsyncEnumerable<int>)>((multiplier, multiplicationResult));
    }

    private static async IAsyncEnumerable<int> DoMultiplication(IAsyncEnumerable<int> values, int multiplier, [EnumeratorCancellation] CancellationToken token)
    {
        await foreach (var value in values.WithCancellation(token))
        {
            yield return value * multiplier;
        }
    }
}

Host the service in the asp.net core application

PS> Install-Package ServiceModel.Grpc.AspNetCore
var builder = WebApplication.CreateBuilder();

// enable ServiceModel.Grpc
builder.Services.AddServiceModelGrpc();

var app = builder.Build();

// bind Calculator service
app.MapGrpcService<Calculator>();

Integrate with Swagger, see example

UI demo

Host the service in Grpc.Core.Server

PS> Install-Package ServiceModel.Grpc.SelfHost
var server = new Grpc.Core.Server
{
    Ports = { new ServerPort("localhost", 5000, ...) }
};

// bind Calculator service
server.Services.AddServiceModelTransient(() => new Calculator());

Server filters

see example

var builder = WebApplication.CreateBuilder();

// setup filter life time
builder.Services.AddSingleton<LoggingServerFilter>();

// attach the filter globally
builder.Services.AddServiceModelGrpc(options =>
{
	options.Filters.Add(1, provider => provider.GetRequiredService<LoggingServerFilter>());
});

internal sealed class LoggingServerFilter : IServerFilter
{
    private readonly ILoggerFactory _loggerFactory;

    public LoggingServerFilter(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public async ValueTask InvokeAsync(IServerFilterContext context, Func<ValueTask> next)
    {
        // create logger with a service name
        var logger = _loggerFactory.CreateLogger(context.ServiceInstance.GetType().Name);

        // log input
        logger.LogInformation("begin {0}", context.ContractMethodInfo.Name);
        foreach (var entry in context.Request)
        {
            logger.LogInformation("input {0} = {1}", entry.Key, entry.Value);
        }

        try
        {
            // invoke all other filters in the stack and the service method
            await next().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            // log exception
            logger.LogError("error {0}: {1}", context.ContractMethodInfo.Name, ex);
            throw;
        }

        // log output
        logger.LogInformation("end {0}", context.ContractMethodInfo.Name);
        foreach (var entry in context.Response)
        {
            logger.LogInformation("output {0} = {1}", entry.Key, entry.Value);
        }
    }
}

NuGet feed <a name="nuget"></a>

Name | Package | Description -----| :------ | :---------- ServiceModel.Grpc | Version | main functionality, basic Grpc.Core.Api extensions and ClientFactory. ClientFactory is fully compatible with Grpc.Net.Client. ServiceModel.Grpc.Client.DependencyInjection | Version | Dependency injection extensions for ClientFactory and Grpc.Net.ClientFactory ServiceModel.Grpc.AspNetCore | Version | Grpc.AspNetCore.Server extensions ServiceModel.Grpc.AspNetCore.Swashbuckle | Version | Swagger integration, based on Swashbuckle.AspNetCore ServiceModel.Grpc.AspNetCore.NSwag | Version | Swagger integration, based on NSwag ServiceModel.Grpc.SelfHost | [![Version](https://img.shields.io/nuget/vpre/ServiceModel.Grpc.SelfH

Related Skills

View on GitHub
GitHub Stars102
CategoryDevelopment
Updated2mo ago
Forks13

Languages

C#

Security Score

95/100

Audited on Jan 21, 2026

No findings