Skip to content

Commit 60de0f0

Browse files
author
Meir Kriheli
committed
make binder route all inherited method using the same main service contract name.
1 parent f98a835 commit 60de0f0

File tree

3 files changed

+86
-15
lines changed

3 files changed

+86
-15
lines changed

src/protobuf-net.Grpc.Reflection/SchemaGenerator.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,14 @@ static Type ApplySubstitutes(Type type)
105105

106106
private static MethodInfo[] GetMethodsRecursively(ServiceBinder serviceBinder, Type contractType)
107107
{
108-
var methods = contractType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
109-
var baseContractInterfaces =
110-
contractType.GetInterfaces()
111-
.Where(cType => serviceBinder.IsServiceContract(cType, out _)); // only the ones marked as contract type
108+
var includingInheritedInterfaces = ContractOperation.ExpandInterfaces(contractType);
112109

113-
var inheritedMethods = baseContractInterfaces.SelectMany(cType => GetMethodsRecursively(serviceBinder, cType)).ToArray();
114-
if (inheritedMethods.Any())
115-
return inheritedMethods.Concat(methods).ToArray();
110+
var inheritedMethods = includingInheritedInterfaces
111+
.Where(cType => serviceBinder.IsServiceContract(cType, out _)) // only the ones marked as contract type
112+
.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Instance))
113+
.ToArray();
116114

117-
return methods;
115+
return inheritedMethods;
118116
}
119117
}
120118
}

src/protobuf-net.Grpc/Configuration/ServerBinder.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,23 @@ public int Bind(object state, Type serviceType, BinderConfiguration? binderConfi
3636
int totalCount = 0;
3737
object?[]? argsBuffer = null;
3838
Type[] typesBuffer = Array.Empty<Type>();
39-
string? serviceName;
40-
if (binderConfiguration == null) binderConfiguration = BinderConfiguration.Default;
41-
var serviceContracts = typeof(IGrpcService).IsAssignableFrom(serviceType)
42-
? new HashSet<Type> { serviceType }
43-
: ContractOperation.ExpandInterfaces(serviceType);
39+
40+
binderConfiguration ??= BinderConfiguration.Default;
41+
42+
if (binderConfiguration.Binder.IsServiceContract(serviceType, out var serviceName) == false)
43+
{
44+
// TODO: shall we assert the following? (currently it will fail some tests)
45+
//throw new ArgumentException($"Type {serviceType.Name} is not defined as contract service", nameof(serviceType));
46+
}
47+
48+
var serviceContracts = ContractOperation.ExpandInterfaces(serviceType);
4449

4550
bool serviceImplSimplifiedExceptions = serviceType.IsDefined(typeof(SimpleRpcExceptionsAttribute));
4651
foreach (var serviceContract in serviceContracts)
4752
{
48-
if (!binderConfiguration.Binder.IsServiceContract(serviceContract, out serviceName)) continue;
53+
// Do not take inherited interfaces names!
54+
// The main serviceName will be used as routing identifier to all inherited methods!
55+
if (!binderConfiguration.Binder.IsServiceContract(serviceContract, out _)) continue;
4956

5057
var serviceContractSimplifiedExceptions = serviceImplSimplifiedExceptions || serviceContract.IsDefined(typeof(SimpleRpcExceptionsAttribute));
5158
int svcOpCount = 0;

tests/protobuf-net.Grpc.Reflection.Test/SchemaGeneration.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
using ProtoBuf.Grpc.Reflection;
55
using System;
66
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
79
using System.Runtime.Serialization;
8-
using System.ServiceModel;
910
using System.Threading.Tasks;
11+
using Google.Protobuf.Reflection;
12+
using Grpc.Core;
1013
using Xunit;
1114
using Xunit.Abstractions;
1215

@@ -124,7 +127,63 @@ public interface INotAService
124127
ValueTask NotAServiceAsyncEmpty();
125128
void NotAServiceSyncEmpty();
126129
}
130+
131+
[Theory]
132+
[InlineData(typeof(IMyService))]
133+
[InlineData(typeof(IMyInheritedService))]
134+
[InlineData(typeof(IMyAnotherLevelOfInheritedService))]
135+
public void CompareRouteTable(Type type)
136+
{
137+
// 1: use the existing binder logic to build the routes, using the server logic
138+
var binder = new TestBinder();
139+
binder.Bind(type, type, BinderConfiguration.Default);
140+
var viaBinder = binder.Collect();
141+
foreach (var method in viaBinder) Log(method);
142+
Log("");
143+
144+
145+
// 2: create a schema and parse it for equivalece
146+
var generator = new SchemaGenerator();
147+
var schema = generator.GetSchema(type);
148+
Log(schema);
149+
var fds = new FileDescriptorSet();
150+
fds.Add("my.proto", source: new StringReader(schema));
151+
fds.Process();
152+
Assert.Empty(fds.GetErrors());
153+
var viaSchema = new List<string>(viaBinder.Length);
154+
var file = fds.Files.Single(static x => x.IncludeInOutput);
155+
foreach (var service in file.Services)
156+
{
157+
var svcName = string.IsNullOrEmpty(file.Package) ? service.Name : $"{file.Package}.{service.Name}";
158+
foreach (var method in service.Methods)
159+
{
160+
viaSchema.Add($"/{svcName}/{method.Name}");
161+
}
162+
}
163+
viaSchema.Sort();
164+
165+
Assert.Equal(string.Join(Environment.NewLine, viaBinder), string.Join(Environment.NewLine, viaSchema));
166+
167+
}
127168

169+
class TestBinder : ServerBinder
170+
{
171+
private readonly List<string> _methods = new List<string>();
172+
protected override bool TryBind<TService, TRequest, TResponse>(ServiceBindContext bindContext, Method<TRequest, TResponse> method, MethodStub<TService> stub)
173+
{
174+
_methods.Add(method.FullName);
175+
return true;
176+
}
177+
178+
public string[] Collect()
179+
{
180+
_methods.Sort();
181+
var arr = _methods.ToArray();
182+
_methods.Clear(); // reset
183+
return arr;
184+
}
185+
}
186+
128187
[Service]
129188
public interface ISomeGenericService<in TGenericRequest, TGenericResult>
130189
{
@@ -143,6 +202,13 @@ public interface IMyInheritedService : IMyService, ISomeGenericService<MyRequest
143202
void InheritedSyncEmpty();
144203
}
145204

205+
206+
[Service]
207+
public interface IMyAnotherLevelOfInheritedService : IMyInheritedService
208+
{
209+
ValueTask<MyResponse> AnotherMethod(MyRequest request, CallContext callContext = default);
210+
}
211+
146212
[DataContract]
147213
public class MyRequest
148214
{

0 commit comments

Comments
 (0)