Skip to content

Commit

Permalink
Move GetMetadata to the ServiceBinder to allow override (protobuf…
Browse files Browse the repository at this point in the history
  • Loading branch information
Euan-McVie committed Sep 21, 2020
1 parent 27345e1 commit d88e62e
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 65 deletions.
76 changes: 18 additions & 58 deletions src/protobuf-net.Grpc/Configuration/ServerBinder.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using Grpc.Core;
using ProtoBuf.Grpc.Internal;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Grpc.Core;
using ProtoBuf.Grpc.Internal;

namespace ProtoBuf.Grpc.Configuration
{
Expand Down Expand Up @@ -49,7 +49,7 @@ public int Bind(object state, Type serviceType, BinderConfiguration? binderConfi

var serviceContractSimplifiedExceptions = serviceImplSimplifiedExceptions || serviceContract.IsDefined(typeof(SimpleRpcExceptionsAttribute));
int svcOpCount = 0;
var bindCtx = new ServiceBindContext(serviceContract, serviceType, state);
var bindCtx = new ServiceBindContext(serviceContract, serviceType, state, binderConfiguration.Binder);
foreach (var op in ContractOperation.FindOperations(binderConfiguration, serviceContract, this))
{
if (ServerInvokerLookup.TryGetValue(op.MethodType, op.Context, op.Result, op.Void, out var invoker)
Expand Down Expand Up @@ -271,77 +271,37 @@ protected internal sealed class ServiceBindContext
/// The caller-provided state for this operation
/// </summary>
public object State { get; }

/// <summary>
/// The service binder to use.
/// </summary>
public ServiceBinder ServiceBinder { get; }

/// <summary>
/// The service contract interface type
/// </summary>
public Type ContractType { get; }

/// <summary>
/// The concrete service type
/// </summary>
public Type ServiceType { get; }

private InterfaceMapping? _map;
private InterfaceMapping GetMap() // lazily memoized
=> _map ??= ServiceType.GetInterfaceMap(ContractType);
internal ServiceBindContext(Type contractType, Type serviceType, object state)
internal ServiceBindContext(Type contractType, Type serviceType, object state, ServiceBinder serviceBinder)
{
State = state;
ServiceBinder = serviceBinder;
ContractType = contractType;
ServiceType = serviceType;
}

/// <summary>
/// Gets the implementing method from a method definition
/// </summary>
public MethodInfo? GetImplementation(MethodInfo serviceMethod)
{
if (ContractType != ServiceType & serviceMethod is object)
{
var map = GetMap();
var from = map.InterfaceMethods;
var to = map.TargetMethods;
int end = Math.Min(from.Length, to.Length);
for (int i = 0; i < end; i++)
{
if (from[i] == serviceMethod) return to[i];
}
}
return null;
}

/// <summary>
/// Gets the metadata associated with a specific contract method
/// <para>Gets the metadata associated with a specific contract method.</para>
/// <para>Note: Later is higher priority in the code that consumes this.</para>
/// </summary>
public List<object> GetMetadata(MethodInfo method)
{
// consider the various possible sources of distinct metadata
object[]
contractType = ContractType.GetCustomAttributes(inherit: true),
contractMethod = method.GetCustomAttributes(inherit: true),
serviceType = Array.Empty<object>(),
serviceMethod = Array.Empty<object>();
if (ContractType != ServiceType & ContractType.IsInterface & ServiceType.IsClass)
{
serviceType = ServiceType.GetCustomAttributes(inherit: true);
serviceMethod = GetImplementation(method)?.GetCustomAttributes(inherit: true)
?? Array.Empty<object>();
}

// note: later is higher priority in the code that consumes this, but
// GetAttributes() is "most derived to least derived", so: add everything
// backwards, then reverse
var metadata = new List<object>(
contractType.Length + contractMethod.Length +
serviceType.Length + serviceMethod.Length);

metadata.AddRange(serviceMethod);
metadata.AddRange(serviceType);
metadata.AddRange(contractMethod);
metadata.AddRange(contractType);
metadata.Reverse();
return metadata;
}
/// <returns>Prioritised list of metadata.</returns>
public IList<object> GetMetadata(MethodInfo method)
=> ServiceBinder.GetMetadata(method, ContractType, ServiceType);
}
}

}
70 changes: 68 additions & 2 deletions src/protobuf-net.Grpc/Configuration/ServiceBinder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using ProtoBuf.Grpc.Internal;
using System;
using System;
using System.Collections.Generic;
using System.Reflection;
using ProtoBuf.Grpc.Internal;

namespace ProtoBuf.Grpc.Configuration
{
Expand All @@ -18,6 +19,17 @@ public class ServiceBinder
/// </summary>
protected ServiceBinder() { }

private Dictionary<Type, InterfaceMapping> _map = new Dictionary<Type, InterfaceMapping>();
private InterfaceMapping GetMap(Type contractType, Type serviceType)
{
if (!_map.TryGetValue(contractType, out var interfaceMapping))
{
interfaceMapping = serviceType.GetInterfaceMap(contractType);
_map[contractType] = interfaceMapping;
}
return interfaceMapping;
}

/// <summary>
/// Gets the default name for a potential service-contract
/// </summary>
Expand Down Expand Up @@ -156,5 +168,59 @@ public virtual bool IsOperationContract(MethodInfo method, out string? name)
name = opName;
return !string.IsNullOrWhiteSpace(name);
}

/// <summary>
/// <para>Gets the metadata associated with a specific contract method.</para>
/// <para>Note: Later is higher priority in the code that consumes this.</para>
/// </summary>
/// <returns>Prioritised list of metadata.</returns>
public virtual IList<object> GetMetadata(MethodInfo method, Type contractType, Type serviceType)
{
// consider the various possible sources of distinct metadata
object[]
contractTypeAtt = contractType.GetCustomAttributes(inherit: true),
contractMethodAtt = method.GetCustomAttributes(inherit: true),
serviceTypeAtt = Array.Empty<object>(),
serviceMethodAtt = Array.Empty<object>();
if (contractType != serviceType & contractType.IsInterface & serviceType.IsClass)
{
serviceTypeAtt = serviceType.GetCustomAttributes(inherit: true);
serviceMethodAtt = GetMethodImplementation(method, contractType, serviceType)?.GetCustomAttributes(inherit: true)
?? Array.Empty<object>();
}

// note: later is higher priority in the code that consumes this, but
// GetAttributes() is "most derived to least derived", so: add everything
// backwards, then reverse
var metadata = new List<object>(
contractTypeAtt.Length + contractMethodAtt.Length +
serviceTypeAtt.Length + serviceMethodAtt.Length);

metadata.AddRange(serviceMethodAtt);
metadata.AddRange(serviceTypeAtt);
metadata.AddRange(contractMethodAtt);
metadata.AddRange(contractTypeAtt);
metadata.Reverse();
return metadata;
}

/// <summary>
/// Gets the implementing method from a method definition
/// </summary>
public MethodInfo? GetMethodImplementation(MethodInfo serviceMethod, Type contractType, Type serviceType)
{
if (contractType != serviceType & serviceMethod is object)
{
var map = GetMap(contractType, serviceType);
var from = map.InterfaceMethods;
var to = map.TargetMethods;
int end = Math.Min(from.Length, to.Length);
for (int i = 0; i < end; i++)
{
if (from[i] == serviceMethod) return to[i];
}
}
return null;
}
}
}
10 changes: 5 additions & 5 deletions tests/protobuf-net.Grpc.Test/AttributeDetection.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using ProtoBuf.Grpc.Internal;
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using ProtoBuf.Grpc.Configuration;
using ProtoBuf.Grpc.Internal;
using Xunit;
using static ProtoBuf.Grpc.Configuration.ServerBinder;

Expand Down Expand Up @@ -50,11 +51,10 @@ public class AttributeDetection
"Interface,ExplicitInterfaceMethod,BaseType,Service,ExplicitServiceMethod")]
public void AttributesDetectedWherever(string methodName, string expected)
{
var ctx = new ServiceBindContext(typeof(ISomeService), typeof(SomeServer), "n/a");
var ctx = new ServiceBindContext(typeof(ISomeService), typeof(SomeServer), "n/a", ServiceBinder.Default);
var method = typeof(ISomeService).GetMethod(methodName)!;
var actual = string.Join(",", ctx.GetMetadata(method).OfType<SomethingAttribute>().Select(x => x.Value));
Assert.Equal(expected, actual);

}

[Theory]
Expand Down Expand Up @@ -168,6 +168,6 @@ public class BarAttribute : Attribute
}
}



}

0 comments on commit d88e62e

Please sign in to comment.