gRPC est un framework RPC open source et performant développé par Google, utilisant HTTP/2 comme protocole de transport. Il supporte le cross-plateforme et le multilinguage. Lorsqu’un projet contient de nombreux services gRPC, l’enregistrement individuel deviant fastidieux. Cet article propose une solution d’enregistrement en masse par réflexion et attributs personnalsiés.
La méthode standard pour injecter un service gRPC dans .NET Core est MapGrpcService<TService>, une méthode générique statique de la classe GrpcEndpointRouteBuilderExtensions. Voici sa signature :
public static GrpcServiceEndpointConventionBuilder MapGrpcService<TService>(this IEndpointRouteBuilder builder) where TService : class;
L’implémentation vérifie d’abord que les services gRPC sont bien enregistrés dans le conteneur DI, puis utilise ServiceRouteBuilder<TService> pour construire les endpoints. Nous pouvons donc appeler cette méthode par réflexion en passant le type du service et le IEndpointRouteBuilder.
Préparons un service simple. Le fichier .proto suivant définit le contrat :
syntax = "proto3";
option csharp_namespace = "GrpcServer";
package Greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
enum Language {
en_us = 0;
zh_cn = 1;
}
Language lang = 2;
}
message HelloReply {
string message = 1;
int32 number = 2;
int32 extra = 3;
}
Implémentons le service :
public class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
string greeting = request.Lang switch
{
HelloRequest.Types.Language.EnUs => "Hello",
HelloRequest.Types.Language.ZhCn => "你好",
_ => "Hi"
};
return Task.FromResult(new HelloReply
{
Message = $"{greeting} {request.Name}",
Number = new Random().Next(1, 1000)
});
}
}
Pour l’enregistrement en masse, nous créons une méthode d’extension qui parcourt l’assembly courant, récupère les classes portant un attribut personnalisé GrpcServiceAttribute et appelle MapGrpcService par réflexion.
Définissons d’abord l’attribut :
[AttributeUsage(AttributeTargets.Class)]
public class GrpcServiceAttribute : Attribute
{
public bool Enabled { get; set; } = true;
}
Marquons ensuite notre service :
[GrpcService(Enabled = true)]
public class GreeterService : Greeter.GreeterBase { ... }
Voici la méthode d’extension pour l’enregistrement :
public static class GrpcEndpointExtensions
{
public static void RegisterGrpcServices(this IEndpointRouteBuilder endpoints, Assembly assembly = null)
{
assembly ??= Assembly.GetCallingAssembly();
var serviceTypes = assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttribute<GrpcServiceAttribute>() != null);
foreach (var type in serviceTypes)
{
var method = typeof(GrpcEndpointRouteBuilderExtensions)
.GetMethod(nameof(GrpcEndpointRouteBuilderExtensions.MapGrpcService))
?.MakeGenericMethod(type);
method?.Invoke(null, new object[] { endpoints });
}
}
public static void UseAutoGrpcServices(this IApplicationBuilder app)
{
app.UseEndpoints(endpoints =>
{
endpoints.RegisterGrpcServices();
});
}
}
La méthode RegisterGrpcServices filtre les types avec l’attribut et appelle MapGrpcService via réflexion. Aucune maintenance des noms de classe n’est nécessaire.
Enfin, dans Startup.cs, ajoutez :
app.UseAutoGrpcServices();
Ainsi, tous les services marqués avec [GrpcService] sont automatiquement enregistrés. Cette approche est maintenable et scalable.