-
-
Notifications
You must be signed in to change notification settings - Fork 107
/
ServicesExtensions.cs
87 lines (81 loc) · 4.25 KB
/
ServicesExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using Grpc.AspNetCore.Server;
using Grpc.AspNetCore.Server.Model;
using Grpc.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using ProtoBuf.Grpc.Configuration;
using System;
using System.Collections.Generic;
namespace ProtoBuf.Grpc.Server
{
/// <summary>
/// Provides extension methods to the IServiceCollection API
/// </summary>
public static class ServicesExtensions
{
/// <summary>
/// Registers a provider that can recognize and handle code-first services
/// </summary>
public static void AddCodeFirstGrpc(this IServiceCollection services) => AddCodeFirstGrpc(services, null);
/// <summary>
/// Registers a provider that can recognize and handle code-first services
/// </summary>
public static IGrpcServerBuilder AddCodeFirstGrpc(this IServiceCollection services, Action<GrpcServiceOptions>? configureOptions)
{
var builder = configureOptions == null ? services.AddGrpc() : services.AddGrpc(configureOptions);
services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IServiceMethodProvider<>), typeof(CodeFirstServiceMethodProvider<>)));
return builder;
}
private sealed class CodeFirstServiceMethodProvider<TService> : IServiceMethodProvider<TService> where TService : class
{
private readonly ILogger<CodeFirstServiceMethodProvider<TService>> _logger;
private readonly BinderConfiguration? _binderConfiguration;
public CodeFirstServiceMethodProvider(ILoggerFactory loggerFactory, BinderConfiguration? binderConfiguration = null)
{
_binderConfiguration = binderConfiguration;
_logger = loggerFactory.CreateLogger<CodeFirstServiceMethodProvider<TService>>();
}
void IServiceMethodProvider<TService>.OnServiceMethodDiscovery(ServiceMethodProviderContext<TService> context)
{
int count = Binder.Instance.Bind<TService>(context, _binderConfiguration);
if (count != 0) _logger.Log(LogLevel.Information, "RPC services being provided by {0}: {1}", typeof(TService), count);
}
}
private sealed class Binder : ServerBinder
{
private Binder() { }
public static readonly Binder Instance = new Binder();
protected override bool TryBind<TService, TRequest, TResponse>(object state, Method<TRequest, TResponse> method, MethodStub<TService> stub)
where TService : class
where TRequest : class
where TResponse : class
{
var metadata = new List<object>();
// Add type metadata first so it has a lower priority
metadata.AddRange(typeof(TService).GetCustomAttributes(inherit: true));
// Add method metadata last so it has a higher priority
metadata.AddRange(stub.Method.GetCustomAttributes(inherit: true));
var context = (ServiceMethodProviderContext<TService>)state;
switch (method.Type)
{
case MethodType.Unary:
context.AddUnaryMethod(method, metadata, stub.CreateDelegate<UnaryServerMethod<TService, TRequest, TResponse>>());
break;
case MethodType.ClientStreaming:
context.AddClientStreamingMethod(method, metadata, stub.CreateDelegate<ClientStreamingServerMethod<TService, TRequest, TResponse>>());
break;
case MethodType.ServerStreaming:
context.AddServerStreamingMethod(method, metadata, stub.CreateDelegate<ServerStreamingServerMethod<TService, TRequest, TResponse>>());
break;
case MethodType.DuplexStreaming:
context.AddDuplexStreamingMethod(method, metadata, stub.CreateDelegate<DuplexStreamingServerMethod<TService, TRequest, TResponse>>());
break;
default:
return false;
}
return true;
}
}
}
}