上一节我们把服务路由改由客户端传入了,但是每次都要在客户端这里写命名空间啥的比较麻烦,这应该在写方法的时候就应该规定好。这时候特性就派上用场了。
想要了解特性的使用的请参考这篇文章:【.net 深呼吸】自定义特性(Attribute)的实现与检索方法
1、首先添加两个特性类,一个用于接口,一个用于方法
namespace AidenGRPC.RPCBase.CommonAttribute { [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] public class ServiceRouteAttribute : Attribute { public ServiceRouteAttribute(string _namespace,string _classname) { NameSpace = _namespace; ClassName = _classname; } public string NameSpace { get; set; } public string ClassName { get; set; } } }
namespace AidenGRPC.RPCBase.CommonAttribute { [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public class ServerMethodAttribute : Attribute { public ServerMethodAttribute(string _methodname) { MethodName = _methodname; } public string MethodName { get; set; } } }
2、添加一个存放接口的程序集,新增一个接口并添加上特性,绑定好此接口的实现类和实现方法名。
namespace AidenGRPC.IModule { [ServiceRoute("AidenGRPC.Module", "SayHelleServer")] public interface ISayHelleServer { [ServerMethod("SayHello")] string SayHello(string name); } }
3、接下来就是要解析这个接口的特性,并将解析完的特性添加到dictionary里面并作为参数直接调用服务端服务。
那么在给接口的实例调用sayhello的时候自动反射调用服务端服务呢?Castle.DynamicProxy这个dll里提供了一种叫动态代理的东西。
ProxyGenerator这个类实现了一个叫 CreateInterfaceProxyWithoutTarget的方法,这个方法需要传入一个实现IInterceptor接口的类。
这样,只需要将接口的实现绑定成这个方法,就可以完成自动解析attribute的功能。
public class ServicesProxy { private static ProxyGenerator generator = new ProxyGenerator(); public static TInterface GetService<TInterface>(string ip, int port) where TInterface : class { return generator.CreateInterfaceProxyWithoutTarget<TInterface>(new GRPCInterceptor(ip, port)); } }
public class GRPCInterceptor : IInterceptor { public string Ip { get; set; } public int Port { get; set; } public GRPCInterceptor(string ip, int port) { Ip = ip; Port = port; } public void Intercept(IInvocation invocation) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); ServerMethodAttribute methodAttr = invocation.Method.GetCustomAttributes<ServerMethodAttribute>().FirstOrDefault<ServerMethodAttribute>(); ServiceRouteAttribute routeAttr = invocation.Method.DeclaringType.GetCustomAttributes<ServiceRouteAttribute>().FirstOrDefault<ServiceRouteAttribute>(); if (methodAttr != null) { if (!string.IsNullOrWhiteSpace(methodAttr.MethodName)) { dictionary.Add(ServicesRoute.METHODNAME, methodAttr.MethodName); } } if (routeAttr != null) { if (!string.IsNullOrWhiteSpace(routeAttr.NameSpace)) { dictionary.Add(ServicesRoute.NAMESPACE, routeAttr.NameSpace); } if (!string.IsNullOrWhiteSpace(routeAttr.ClassName)) { dictionary.Add(ServicesRoute.CLASSNAME, routeAttr.ClassName); } } ParameterInfo[] parameters = invocation.Method.GetParameters(); for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameterInfo = parameters[i]; if (!dictionary.ContainsKey(parameterInfo.Name)) { dictionary.Add(parameterInfo.Name, invocation.Arguments[i].ToString()); //不完善,如果参数是个类,那么这里需要用转成json再添加 } else { dictionary[parameterInfo.Name] = invocation.Arguments[i].ToString(); } } if (invocation.Method.ReturnType == typeof(void))) { new Request(Ip, Port).Invoke(dictionary); return; } GrpcResponse responseMsg = new Request(Ip, Port).Invoke(dictionary); invocation.ReturnValue = responseMsg.ResponseMsg; } }
4、依赖注入,我们在客户端main函数里面直接注入一下,试试看效果。
static void Main(string[] args) { var serviceProvider = new ServiceCollection() .AddSingleton(typeof(ISayHelleServer), ServicesProxy.GetService<ISayHelleServer>("127.0.0.1", 30052)) .BuildServiceProvider(); var server = serviceProvider.GetService<ISayHelleServer>(); string rp = server.SayHello("Aiden"); Console.WriteLine(rp); Console.WriteLine("Press any key to exit client..."); Console.ReadKey(); }
5、运行试试看,如下图,能够走通,这样代理就完成了。