BindMethodFinder.cs 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. using Grpc.Core;
  2. using Grpc.Extension.Abstract;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Text;
  7. namespace Grpc.Extension.AspNetCore.Internal
  8. {
  9. internal static class BindMethodFinder
  10. {
  11. private const BindingFlags BindMethodBindingFlags = BindingFlags.Public | BindingFlags.Static;
  12. internal static MethodInfo? GetBindMethod(Type serviceType)
  13. {
  14. if (typeof(IGrpcService).IsAssignableFrom(serviceType))
  15. {
  16. return typeof(GrpcMethodHelper).GetMethod("BindService");
  17. }
  18. else
  19. {
  20. // Prefer finding the bind method using attribute on the generated service
  21. var bindMethodInfo = GetBindMethodUsingAttribute(serviceType);
  22. if (bindMethodInfo == null)
  23. {
  24. // Fallback to searching for bind method using known type hierarchy that Grpc.Tools generates
  25. bindMethodInfo = GetBindMethodFallback(serviceType);
  26. }
  27. return bindMethodInfo;
  28. }
  29. }
  30. internal static MethodInfo? GetBindMethodUsingAttribute(Type serviceType)
  31. {
  32. Type? currentServiceType = serviceType;
  33. BindServiceMethodAttribute? bindServiceMethod;
  34. do
  35. {
  36. // Search through base types for bind service attribute
  37. // We need to know the base service type because it is used with GetMethod below
  38. bindServiceMethod = currentServiceType.GetCustomAttribute<BindServiceMethodAttribute>();
  39. if (bindServiceMethod != null)
  40. {
  41. // Bind method will be public and static
  42. // Two parameters: ServiceBinderBase and the service type
  43. return bindServiceMethod.BindType.GetMethod(
  44. bindServiceMethod.BindMethodName,
  45. BindMethodBindingFlags,
  46. binder: null,
  47. new[] { typeof(ServiceBinderBase), currentServiceType },
  48. Array.Empty<ParameterModifier>());
  49. }
  50. } while ((currentServiceType = currentServiceType.BaseType) != null);
  51. return null;
  52. }
  53. internal static MethodInfo? GetBindMethodFallback(Type serviceType)
  54. {
  55. // Search for the generated service base class
  56. var baseType = GetServiceBaseType(serviceType);
  57. if (baseType == null)
  58. {
  59. return null;
  60. }
  61. // We need to call Foo.BindService from the declaring type.
  62. var declaringType = baseType.DeclaringType;
  63. // The method we want to call is public static void BindService(ServiceBinderBase, BaseType)
  64. return declaringType?.GetMethod(
  65. "BindService",
  66. BindMethodBindingFlags,
  67. binder: null,
  68. new[] { typeof(ServiceBinderBase), baseType },
  69. Array.Empty<ParameterModifier>());
  70. }
  71. private static Type? GetServiceBaseType(Type serviceImplementation)
  72. {
  73. // TService is an implementation of the gRPC service. It ultimately derives from Foo.TServiceBase base class.
  74. // We need to access the static BindService method on Foo which implicitly derives from Object.
  75. var baseType = serviceImplementation.BaseType;
  76. // Handle services that have multiple levels of inheritence
  77. while (baseType?.BaseType?.BaseType != null)
  78. {
  79. baseType = baseType.BaseType;
  80. }
  81. return baseType;
  82. }
  83. }
  84. }