Skip to content

Commit 11c4f76

Browse files
committed
Merge in 'release/7.0' changes
2 parents 2a221e1 + 0e32ba3 commit 11c4f76

File tree

20 files changed

+269
-28
lines changed

20 files changed

+269
-28
lines changed

src/Grpc/JsonTranscoding/perf/Microsoft.AspNetCore.Grpc.Microbenchmarks/Json/JsonReading.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Google.Protobuf.Reflection;
88
using Greet;
99
using Microsoft.AspNetCore.Grpc.JsonTranscoding;
10+
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
1011
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
1112

1213
namespace Microsoft.AspNetCore.Grpc.Microbenchmarks.Json;
@@ -21,7 +22,7 @@ public class JsonReading
2122
public void GlobalSetup()
2223
{
2324
_requestJson = (new HelloRequest() { Name = "Hello world" }).ToString();
24-
_serializerOptions = JsonConverterHelper.CreateSerializerOptions(new JsonContext(new GrpcJsonSettings { WriteIndented = false }, TypeRegistry.Empty));
25+
_serializerOptions = JsonConverterHelper.CreateSerializerOptions(new JsonContext(new GrpcJsonSettings { WriteIndented = false }, TypeRegistry.Empty, new DescriptorRegistry()));
2526
_jsonFormatter = new JsonParser(new JsonParser.Settings(recursionLimit: 100));
2627
}
2728

src/Grpc/JsonTranscoding/perf/Microsoft.AspNetCore.Grpc.Microbenchmarks/Json/JsonWriting.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Google.Protobuf.Reflection;
88
using Greet;
99
using Microsoft.AspNetCore.Grpc.JsonTranscoding;
10+
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
1011
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
1112

1213
namespace Microsoft.AspNetCore.Grpc.Microbenchmarks.Json;
@@ -22,7 +23,7 @@ public void GlobalSetup()
2223
{
2324
_request = new HelloRequest() { Name = "Hello world" };
2425
_serializerOptions = JsonConverterHelper.CreateSerializerOptions(
25-
new JsonContext(new GrpcJsonSettings { WriteIndented = false }, TypeRegistry.Empty));
26+
new JsonContext(new GrpcJsonSettings { WriteIndented = false }, TypeRegistry.Empty, new DescriptorRegistry()));
2627
_jsonFormatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: false));
2728
}
2829

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingOptions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Text.Json;
55
using Google.Protobuf.Reflection;
6+
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
67
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
78

89
namespace Microsoft.AspNetCore.Grpc.JsonTranscoding;
@@ -18,16 +19,19 @@ public sealed class GrpcJsonTranscodingOptions
1819
public GrpcJsonTranscodingOptions()
1920
{
2021
_unaryOptions = new Lazy<JsonSerializerOptions>(
21-
() => JsonConverterHelper.CreateSerializerOptions(new JsonContext(JsonSettings, TypeRegistry)),
22+
() => JsonConverterHelper.CreateSerializerOptions(new JsonContext(JsonSettings, TypeRegistry, DescriptorRegistry)),
2223
LazyThreadSafetyMode.ExecutionAndPublication);
2324
_serverStreamingOptions = new Lazy<JsonSerializerOptions>(
24-
() => JsonConverterHelper.CreateSerializerOptions(new JsonContext(JsonSettings, TypeRegistry), isStreamingOptions: true),
25+
() => JsonConverterHelper.CreateSerializerOptions(new JsonContext(JsonSettings, TypeRegistry, DescriptorRegistry), isStreamingOptions: true),
2526
LazyThreadSafetyMode.ExecutionAndPublication);
2627
}
2728

2829
internal JsonSerializerOptions UnarySerializerOptions => _unaryOptions.Value;
2930
internal JsonSerializerOptions ServerStreamingSerializerOptions => _serverStreamingOptions.Value;
3031

32+
// Registry is set by DI during startup.
33+
internal DescriptorRegistry DescriptorRegistry { get; set; } = default!;
34+
3135
/// <summary>
3236
/// Gets or sets the <see cref="Google.Protobuf.Reflection.TypeRegistry"/> used to lookup types from type names.
3337
/// </summary>

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using Grpc.AspNetCore.Server;
55
using Grpc.AspNetCore.Server.Model;
66
using Microsoft.AspNetCore.Grpc.JsonTranscoding;
7+
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
78
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Binding;
89
using Microsoft.Extensions.DependencyInjection.Extensions;
10+
using Microsoft.Extensions.Options;
911

1012
namespace Microsoft.Extensions.DependencyInjection;
1113

@@ -27,6 +29,8 @@ public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder buil
2729
}
2830

2931
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IServiceMethodProvider<>), typeof(JsonTranscodingServiceMethodProvider<>)));
32+
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<GrpcJsonTranscodingOptions>, GrpcJsonTranscodingOptionsSetup>());
33+
builder.Services.TryAddSingleton<DescriptorRegistry>();
3034

3135
return builder;
3236
}
@@ -45,6 +49,27 @@ public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder buil
4549
}
4650

4751
builder.Services.Configure(configureOptions);
52+
4853
return builder.AddJsonTranscoding();
4954
}
55+
56+
private sealed class GrpcJsonTranscodingOptionsSetup : IConfigureOptions<GrpcJsonTranscodingOptions>
57+
{
58+
private readonly DescriptorRegistry _descriptorRegistry;
59+
60+
public GrpcJsonTranscodingOptionsSetup(DescriptorRegistry descriptorRegistry)
61+
{
62+
_descriptorRegistry = descriptorRegistry;
63+
}
64+
65+
public void Configure(GrpcJsonTranscodingOptions options)
66+
{
67+
if (options == null)
68+
{
69+
throw new ArgumentNullException(nameof(options));
70+
}
71+
72+
options.DescriptorRegistry = _descriptorRegistry;
73+
}
74+
}
5075
}

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ private delegate (RequestDelegate RequestDelegate, List<object> Metadata) Create
3434
private readonly GrpcServiceOptions _globalOptions;
3535
private readonly GrpcServiceOptions<TService> _serviceOptions;
3636
private readonly IGrpcServiceActivator<TService> _serviceActivator;
37-
private readonly GrpcJsonTranscodingOptions _JsonTranscodingOptions;
37+
private readonly GrpcJsonTranscodingOptions _jsonTranscodingOptions;
3838
private readonly ILoggerFactory _loggerFactory;
3939
private readonly ILogger _logger;
4040

@@ -46,15 +46,15 @@ internal JsonTranscodingProviderServiceBinder(
4646
GrpcServiceOptions<TService> serviceOptions,
4747
ILoggerFactory loggerFactory,
4848
IGrpcServiceActivator<TService> serviceActivator,
49-
GrpcJsonTranscodingOptions JsonTranscodingOptions)
49+
GrpcJsonTranscodingOptions jsonTranscodingOptions)
5050
{
5151
_context = context;
5252
_invokerResolver = invokerResolver;
5353
_serviceDescriptor = serviceDescriptor;
5454
_globalOptions = globalOptions;
5555
_serviceOptions = serviceOptions;
5656
_serviceActivator = serviceActivator;
57-
_JsonTranscodingOptions = JsonTranscodingOptions;
57+
_jsonTranscodingOptions = jsonTranscodingOptions;
5858
_loggerFactory = loggerFactory;
5959
_logger = loggerFactory.CreateLogger<JsonTranscodingProviderServiceBinder<TService>>();
6060
}
@@ -168,7 +168,7 @@ private void ProcessHttpRule<TRequest, TResponse>(
168168
methodInvoker,
169169
_loggerFactory,
170170
descriptorInfo,
171-
_JsonTranscodingOptions.UnarySerializerOptions);
171+
_jsonTranscodingOptions.UnarySerializerOptions);
172172

173173
return (callHandler.HandleCallAsync, metadata);
174174
}
@@ -195,7 +195,7 @@ private void ProcessHttpRule<TRequest, TResponse>(
195195
methodInvoker,
196196
_loggerFactory,
197197
descriptorInfo,
198-
_JsonTranscodingOptions.ServerStreamingSerializerOptions);
198+
_jsonTranscodingOptions.ServerStreamingSerializerOptions);
199199

200200
return (callHandler.HandleCallAsync, metadata);
201201
}

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingServiceMethodProvider.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,26 @@ internal sealed partial class JsonTranscodingServiceMethodProvider<TService> : I
1616
private readonly ILogger<JsonTranscodingServiceMethodProvider<TService>> _logger;
1717
private readonly GrpcServiceOptions _globalOptions;
1818
private readonly GrpcServiceOptions<TService> _serviceOptions;
19-
private readonly GrpcJsonTranscodingOptions _JsonTranscodingOptions;
19+
private readonly GrpcJsonTranscodingOptions _jsonTranscodingOptions;
2020
private readonly ILoggerFactory _loggerFactory;
2121
private readonly IGrpcServiceActivator<TService> _serviceActivator;
22+
private readonly DescriptorRegistry _serviceDescriptorRegistry;
2223

2324
public JsonTranscodingServiceMethodProvider(
2425
ILoggerFactory loggerFactory,
2526
IOptions<GrpcServiceOptions> globalOptions,
2627
IOptions<GrpcServiceOptions<TService>> serviceOptions,
2728
IGrpcServiceActivator<TService> serviceActivator,
28-
IOptions<GrpcJsonTranscodingOptions> JsonTranscodingOptions)
29+
IOptions<GrpcJsonTranscodingOptions> jsonTranscodingOptions,
30+
DescriptorRegistry serviceDescriptorRegistry)
2931
{
3032
_logger = loggerFactory.CreateLogger<JsonTranscodingServiceMethodProvider<TService>>();
3133
_globalOptions = globalOptions.Value;
3234
_serviceOptions = serviceOptions.Value;
33-
_JsonTranscodingOptions = JsonTranscodingOptions.Value;
35+
_jsonTranscodingOptions = jsonTranscodingOptions.Value;
3436
_loggerFactory = loggerFactory;
3537
_serviceActivator = serviceActivator;
38+
_serviceDescriptorRegistry = serviceDescriptorRegistry;
3639
}
3740

3841
public void OnServiceMethodDiscovery(ServiceMethodProviderContext<TService> context)
@@ -57,6 +60,8 @@ public void OnServiceMethodDiscovery(ServiceMethodProviderContext<TService> cont
5760

5861
if (serviceDescriptor != null)
5962
{
63+
_serviceDescriptorRegistry.RegisterFileDescriptor(serviceDescriptor.File);
64+
6065
var binder = new JsonTranscodingProviderServiceBinder<TService>(
6166
context,
6267
new ReflectionServiceInvokerResolver<TService>(serviceParameter.ParameterType),
@@ -65,7 +70,7 @@ public void OnServiceMethodDiscovery(ServiceMethodProviderContext<TService> cont
6570
_serviceOptions,
6671
_loggerFactory,
6772
_serviceActivator,
68-
_JsonTranscodingOptions);
73+
_jsonTranscodingOptions);
6974

7075
try
7176
{
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Google.Protobuf.Reflection;
5+
6+
namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
7+
8+
internal sealed class DescriptorRegistry
9+
{
10+
private readonly HashSet<FileDescriptor> _fileDescriptors = new HashSet<FileDescriptor>();
11+
private readonly HashSet<EnumDescriptor> _enumDescriptors = new HashSet<EnumDescriptor>();
12+
13+
public void RegisterFileDescriptor(FileDescriptor fileDescriptor)
14+
{
15+
AddFileDescriptorsRecursive(fileDescriptor);
16+
}
17+
18+
private void AddFileDescriptorsRecursive(FileDescriptor fileDescriptor)
19+
{
20+
var added = _fileDescriptors.Add(fileDescriptor);
21+
22+
// If a descriptor is already added then all its types and dependencies are already be present.
23+
// In this case, exit immediately. This guards against the possibility of cyclical dependencies between files.
24+
if (!added)
25+
{
26+
return;
27+
}
28+
29+
// Non-nested enums.
30+
foreach (var descriptor in fileDescriptor.EnumTypes)
31+
{
32+
_enumDescriptors.Add(descriptor);
33+
}
34+
35+
// Search messages for nested enums.
36+
foreach (var messageDescriptor in fileDescriptor.MessageTypes)
37+
{
38+
AddNestedEnumDescriptorsRecursive(messageDescriptor);
39+
}
40+
41+
// Search imported files.
42+
foreach (var dependencyFile in fileDescriptor.Dependencies)
43+
{
44+
AddFileDescriptorsRecursive(dependencyFile);
45+
}
46+
}
47+
48+
private void AddNestedEnumDescriptorsRecursive(MessageDescriptor messageDescriptor)
49+
{
50+
foreach (var enumDescriptor in messageDescriptor.EnumTypes)
51+
{
52+
_enumDescriptors.Add(enumDescriptor);
53+
}
54+
55+
foreach (var nestedMessageDescriptor in messageDescriptor.NestedTypes)
56+
{
57+
AddNestedEnumDescriptorsRecursive(nestedMessageDescriptor);
58+
}
59+
}
60+
61+
public EnumDescriptor? FindEnumDescriptorByType(Type enumType)
62+
{
63+
foreach (var enumDescriptor in _enumDescriptors)
64+
{
65+
if (enumDescriptor.ClrType == enumType)
66+
{
67+
return enumDescriptor;
68+
}
69+
}
70+
71+
return null;
72+
}
73+
}

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/EnumConverter.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ public EnumConverter(JsonContext context) : base(context)
3737
}
3838
}
3939

40-
private static EnumDescriptor? ResolveEnumDescriptor(Type typeToConvert)
40+
private EnumDescriptor? ResolveEnumDescriptor(Type typeToConvert)
4141
{
42+
// Existing resolve descriptor logic. Use it when possible to minimize change.
4243
var containingType = typeToConvert?.DeclaringType?.DeclaringType;
4344

4445
if (containingType != null)
@@ -56,7 +57,11 @@ public EnumConverter(JsonContext context) : base(context)
5657
}
5758
}
5859

59-
return null;
60+
// Enum types in generated DTOs are regular enum types. That means there is no static Descriptor property
61+
// to get the enum descriptor from.
62+
//
63+
// Search for enum descriptor using the enum type in a registry of descriptors.
64+
return Context.DescriptorRegistry.FindEnumDescriptorByType(typeToConvert!);
6065
}
6166

6267
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)

src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Json/JsonContext.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
77

88
internal sealed class JsonContext
99
{
10-
public JsonContext(GrpcJsonSettings settings, TypeRegistry typeRegistry)
10+
public JsonContext(GrpcJsonSettings settings, TypeRegistry typeRegistry, DescriptorRegistry descriptorRegistry)
1111
{
1212
Settings = settings;
1313
TypeRegistry = typeRegistry;
14+
DescriptorRegistry = descriptorRegistry;
1415
}
1516

1617
public GrpcJsonSettings Settings { get; }
1718
public TypeRegistry TypeRegistry { get; }
19+
public DescriptorRegistry DescriptorRegistry { get; }
1820
}

src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/DynamicGrpcServiceRegistry.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using Google.Api;
56
using Google.Protobuf;
67
using Google.Protobuf.Reflection;
@@ -40,6 +41,11 @@ public Method<TRequest, TResponse> AddUnaryMethod<TRequest, TResponse>(UnaryServ
4041

4142
AddServiceCore(c =>
4243
{
44+
// File descriptor is done in JsonTranscodingServiceMethodProvider.
45+
// Need to replicate that logic here so tests that lookup descriptors are successful.
46+
var descriptorRegistry = _serviceProvider.GetRequiredService<DescriptorRegistry>();
47+
descriptorRegistry.RegisterFileDescriptor(methodDescriptor.File);
48+
4349
var unaryMethod = new UnaryServerMethod<DynamicService, TRequest, TResponse>((service, request, context) => callHandler(request, context));
4450
var binder = CreateJsonTranscodingBinder<TRequest, TResponse>(methodDescriptor, c, new DynamicServiceInvokerResolver(unaryMethod));
4551

0 commit comments

Comments
 (0)