Search code examples
c#asp.net-core.net-coregrpcgrpc-dotnet

Can I receive a scoped dependency for a C# gRPC Client Interceptor?


I am using a gRPC client inside a web API controller method to make a call out to a database and I want all of the calls made to add some common metadata in a transparent fashion. That is, I don't want to have to consciously add Metadata to every gRPC client call. A gRPC Interceptor seems perfect for this but to add that common metadata for each call, I need to resolve a scoped dependency that gives me the data I need to put in the gRPC call metadata.

Just as a similar example, in a .NET Core Middleware, I can resolve scoped dependencies by adding parameters to the InvokeAsync(...) method like this:

    public async Task InvokeAsync(HttpContext context, IMyScopedDependency myService)
    {

However, I am not sure if / how I can do the same thing with a gRPC Client Interceptor as those override methods from the base Interceptor class and I can't add arbitrary extra parameters to them.

Is there some clever way I am missing to inject a scoped lifetime dependency into the interceptor public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(...) method?


Solution

  • I figured this out by doing the following...

    Registering my interceptor with a scoped lifetime:

    builder.Services.AddScoped<MyInterceptor>();
    

    Registering my dependency with a scoped lifetime:

    builder.Services.AddScoped<IMyDependencyService, MyDependencyService>();
    

    In my interceptor, I receive the scoped dependency via DI in the constructor:

    internal class MyInterceptor: Interceptor
    {
        private readonly IMyDependencyService _dependency;
    
        public MyInterceptor(IMyDependencyService dependency)
        {
            _dependency = dependency;
        }
    

    I used Grpc.Net.ClientFactory to register my gRPC client with an interceptor using the InterceptorScope.Client lifetime option:

    services.AddGrpcClient<MyGrpcClient>(o =>
    {
        o.Address = new Uri("http://localhost:5000");
    }).AddInterceptor(Grpc.Net.ClientFactory.InterceptorScope.Client, (sp) => sp.GetRequiredService<MyInterceptor>());
    

    Finally, in my Web API controller, I receive the client via DI in the constructor and then I can use it in the controller methods:

    [ApiController]
    [Route("[controller]")]
    public class TestController : ControllerBase
    {
        private readonly MyGrpcClient _client;
        private readonly ILogger<TestController> _logger;
    
        public TestController(MyGrpcClient client, ILogger<TestController> logger)
        {
            _client = client;
            _logger = logger;
        }