2

I am experimenting with a gRPC service and client using proto files. The advice is to use gRPC client factory integration in .NET Core (https://learn.microsoft.com/en-us/aspnet/core/grpc/clientfactory?view=aspnetcore-3.1). To do this you register the client derived from Grpc.Core.ClientBase that is generated by the Grpc.Tools package, like this:

Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddGrpcClient<MyGrpcClientType>(o =>
        {
            o.Address = new Uri("https://localhost:5001");
        });
    })

My understanding is that MyGrpcClientType is registered with DI as a transient client, meaning a new one is created each time it is injected, but that the client is integrated with the HttpClientFactory, allowing the channel to be reused rather than be created each time.

Now, I would like to use protobuf-net.grpc to generate the client from an interface, which appears to be done like this:

GrpcClientFactory.AllowUnencryptedHttp2 = true;
using var http = GrpcChannel.ForAddress("http://localhost:10042");
var calculator = http.CreateGrpcService<ICalculator>();

If I am correct in thinking that channels are expensive to create, but clients are cheap, how do I achieve integration with the HttpClientFactory (and so reuse of the underlying channel) using protobuf-net.grpc? The above appears to create a GrpcChannel each time I want a client, so what is the correct approach to reusing channels?

Similarly, is it possible to register the protobuf-net.grpc generated service class with the below code in ASP.Net Core?

endpoints.MapGrpcService<MyGrpcServiceType>();

(Please correct any misunderstandings in the above)

Julius
  • 735
  • 1
  • 9
  • 25

2 Answers2

2

Note that you don't need the AllowUnencryptedHttp2 - that's just if you aren't using https, but: you seem to be using https.

On the "similarly"; that should already work - the only bit you might be missing is the call to services.AddCodeFirstGrpc() (usually in Startup.cs, via ConfigureServices).

As for the AddGrpcClient; I would have to investigate. That isn't something that I've explored in the integrations so far. It might be a new piece is needed.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks for your incredibly swift reply Marc - much appreciated. – Julius Mar 26 '20 at 14:58
  • For readers this is an example of the service Startup.cs: https://github.com/protobuf-net/protobuf-net.Grpc/blob/master/examples/pb-net-grpc/Server_CS/Startup.cs – Julius Mar 26 '20 at 14:58
  • Marc - when you have had a chance to look into the HttpClientFactory integration would you post your findings here? I'm keen to understand how it works and what you are thinking. Thanks again. – Julius Mar 26 '20 at 14:59
  • @Julius here's my initial thoughts; how would you feel about using [`CodeFirstClient`](https://github.com/protobuf-net/protobuf-net.Grpc/commit/33c2a75b1ecb38405e847b0bcc797edab86ef173#diff-b6d02e71faaab1563bea903f1009216eR4) in your APIs? if we do *that* I think we can make it work (this is a new experimental API that does not actually exist) – Marc Gravell Mar 26 '20 at 17:45
  • @Julius I have proposed some API changes to `Grpc.Net.ClientFactory` to make this work directly: https://github.com/grpc/grpc-dotnet/pull/837 – Marc Gravell Mar 27 '20 at 11:35
  • Yes a CodeFirstClient makes perfect sense to me. I've read your proposal and that looks good also, but where would the CustomGrpcClientFactory come from? Ideally a default implementation would be available so for most use cases there is no need to create a custom one, or even better, the factory registration would be unnecessary. – Julius Mar 27 '20 at 15:19
  • @Julius oh, that would be a method in my lib - you'd basically call `services.AddCodeFirstClientFactory()` or something – Marc Gravell Mar 27 '20 at 15:22
  • then that proposal sounds great, thanks very much. Let's hope the grpc-dotnet team agree. – Julius Mar 27 '20 at 15:45
  • I've re-read my question and I want to make sure I am really clear on my use case. I would ideally like to use the Typed Client approach to using the HttpClientFactory, where you create a Service Agent (Proxy) class which is capable of configuring the HttpClient itself, and can be hooked into the .Net Core Dependency Injection handling of HttpClients: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1#typed-clients – Julius Mar 30 '20 at 11:54
  • So I would ideally like a way to register the Service Agent class and have a Grpc Client or channel injected, with a way to configure the underlying HttpClient. This would work with your proposed services.AddGrpcClient if T is the type of a Service Agent class, e.g. MyServiceClient, which takes a Grpc client or channel in the ctor. – Julius Mar 30 '20 at 11:55
  • @Julius you'd only use `AddGrpcClient` with something that *is grpc* - that is true of the existing `AddGrpcClient` implementation; of course, how you then daisy chain that into other DI usages is entirely up to you – Marc Gravell Mar 30 '20 at 12:11
1

The Client Factory support not exists, and works exactly like documented here except you register with the method

services.AddCodeFirstGrpcClient<IMyService>(o =>
{
    o.Address = new Uri("...etc...");
});
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900