├── docs ├── CNAME ├── _config.yml ├── install.md ├── index.md ├── server.md ├── server.org ├── duplex.org └── duplex.md ├── Simple ├── Duplex │ ├── Client │ │ ├── CodeGenerate_dll │ │ ├── appsettings.json │ │ ├── Client.csproj │ │ └── Program.cs │ ├── Server │ │ ├── Kadder.Grpc.Server_dll │ │ ├── appsettings.json │ │ ├── NoteServicer.cs │ │ ├── Program.cs │ │ └── Server.csproj │ ├── Protocol │ │ ├── INoteServicer.cs │ │ ├── Protocols │ │ │ ├── CreateNoteResponse.cs │ │ │ └── CreateNoteRequest.cs │ │ └── Protocol.csproj │ └── Duplex.sln └── Server │ ├── Kadder.Grpc.Server_dll │ ├── libgrpc_csharp_ext.arm64.dylib │ ├── appsettings.json │ ├── Protocols │ ├── CreateNoteRequest.cs │ └── CreateNoteResponse.cs │ ├── NoteServicer.cs │ ├── Server.csproj │ ├── Program.cs │ └── Server.sln ├── .travis.yml ├── Kadder ├── Messaging │ ├── MessagingContext.cs │ ├── KServicerAttribute.cs │ ├── EmptyMessage.cs │ ├── EmptyMessageResult.cs │ ├── IMessagingContext.cs │ ├── KServicer.cs │ └── NoGRPCMethodAttribute.cs ├── Streaming │ ├── IAsyncRequestStream.cs │ ├── IAsyncStream.cs │ ├── IDuplexStream.cs │ ├── IAsyncResponseStream.cs │ ├── IStreamReader.cs │ └── IStreamWriter.cs ├── Utils │ ├── KadderOptions.cs │ ├── IObjectScope.cs │ ├── OperationType.cs │ ├── IBinarySerializer.cs │ ├── IObjectProvider.cs │ ├── IMessagingServicer.cs │ ├── IJsonSerializer.cs │ ├── NullJsonSerializer.cs │ ├── NewtonsoftJsonSerializer.cs │ ├── ObjectScope.cs │ ├── ObjectProvider.cs │ ├── Message.cs │ ├── RefelectionHelper.cs │ ├── ServicerHelper.cs │ ├── ProtobufBinarySerializer.cs │ ├── Bcl.cs │ ├── InvitationAlgorithm.cs │ ├── Ensure.cs │ └── Exetension.cs ├── Grpc │ ├── CallType.cs │ ├── Server │ │ ├── IGrpcServices.cs │ │ ├── AsyncRequestStream.cs │ │ ├── AsyncResponseStream.cs │ │ ├── GrpcServerOptions.cs │ │ ├── StreamExtension.cs │ │ ├── GrpcServerBuilder.cs │ │ ├── HostExtension.cs │ │ └── ServicerProxyGenerator.cs │ ├── Client │ │ ├── AsyncResponseStream.cs │ │ ├── ClientStreamResult.cs │ │ ├── AsyncRequestStream.cs │ │ ├── DuplexStreamResult.cs │ │ ├── Options │ │ │ ├── GrpcChannelOptions.cs │ │ │ └── GrpcClientOptions.cs │ │ ├── Client.cs │ │ ├── StreamMessage.cs │ │ ├── HostExtension.cs │ │ ├── Extension.cs │ │ ├── ClientBuilder.cs │ │ ├── ServiceExtension.cs │ │ ├── GrpcProxyer.cs │ │ ├── ServicerInvoker.cs │ │ └── ServicerProxyGenerator.cs │ └── Helper.cs ├── GrpcMethodCheck.cs ├── KadderBuilder.cs ├── Kadder.csproj ├── GrpcOptions.cs └── GrpcConfiguration.cs ├── .gitignore ├── Kadder.Maui ├── Platforms │ ├── iOS │ │ └── PlatformClass1.cs │ ├── Android │ │ └── PlatformClass1.cs │ ├── Windows │ │ └── PlatformClass1.cs │ ├── MacCatalyst │ │ └── PlatformClass1.cs │ └── Tizen │ │ └── PlatformClass1.cs ├── MauiHostExtension.cs └── Kadder.Maui.csproj ├── Kadder.Test ├── UnitTest1.cs ├── Grpc │ ├── HelperTest.cs │ └── Client │ │ └── ServicerProxyGeneratorTest.cs └── Kadder.Test.csproj ├── LICENSE ├── .vscode ├── launch.json └── tasks.json ├── README.md └── Kadder.sln /docs/CNAME: -------------------------------------------------------------------------------- 1 | kadder.wanbin.tech -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /Simple/Duplex/Client/CodeGenerate_dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binwan-dev/Kadder/HEAD/Simple/Duplex/Client/CodeGenerate_dll -------------------------------------------------------------------------------- /Simple/Server/Kadder.Grpc.Server_dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binwan-dev/Kadder/HEAD/Simple/Server/Kadder.Grpc.Server_dll -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: Kadder.sln 3 | dotnet: 2.2.401 4 | script: 5 | - dotnet restore 6 | - dotnet build 7 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | ### 安装 Kadder 2 | `dotnet add package Kadder` 3 | 4 | ### 安装 Proto2Csharp 5 | `dotnet tool install --global proto2csharp` -------------------------------------------------------------------------------- /Simple/Duplex/Server/Kadder.Grpc.Server_dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binwan-dev/Kadder/HEAD/Simple/Duplex/Server/Kadder.Grpc.Server_dll -------------------------------------------------------------------------------- /Simple/Server/libgrpc_csharp_ext.arm64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binwan-dev/Kadder/HEAD/Simple/Server/libgrpc_csharp_ext.arm64.dylib -------------------------------------------------------------------------------- /Kadder/Messaging/MessagingContext.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Messaging 2 | { 3 | public class MessagingContext : IMessagingContext 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin/ 3 | obj/ 4 | .builder-cache/ 5 | .dll/ 6 | /.vs/Atlantis.Grpc/v15 7 | .idea/ 8 | /.vs/Kadder/v16 9 | /.vs/Kadder/DesignTimeBuild 10 | .cache/ -------------------------------------------------------------------------------- /Kadder.Maui/Platforms/iOS/PlatformClass1.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Maui; 2 | 3 | // All the code in this file is only included on iOS. 4 | public class PlatformClass1 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Kadder/Messaging/KServicerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder 4 | { 5 | public class KServicerAttribute : Attribute 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Streaming/IAsyncRequestStream.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Streaming 2 | { 3 | public interface IAsyncRequestStream : IAsyncStream where T : class 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Kadder/Streaming/IAsyncStream.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Kadder.Streaming 4 | { 5 | public interface IAsyncStream 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Utils/KadderOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Utils 2 | { 3 | public class KadderOptions 4 | { 5 | public static IObjectProvider KadderObjectProvider; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Kadder.Maui/Platforms/Android/PlatformClass1.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Maui; 2 | 3 | // All the code in this file is only included on Android. 4 | public class PlatformClass1 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Kadder.Maui/Platforms/Windows/PlatformClass1.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Maui; 2 | 3 | // All the code in this file is only included on Windows. 4 | public class PlatformClass1 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Kadder.Maui/Platforms/MacCatalyst/PlatformClass1.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Maui; 2 | 3 | // All the code in this file is only included on Mac Catalyst. 4 | public class PlatformClass1 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Kadder.Test/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Kadder.Test; 4 | 5 | public class UnitTest1 6 | { 7 | [Fact] 8 | public void Test1() 9 | { 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /Kadder/Streaming/IDuplexStream.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Streaming 2 | { 3 | public interface IDuplexStream where TRequest : class where TResponse : class 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Kadder/Messaging/EmptyMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ProtoBuf; 3 | 4 | namespace Kadder.Messaging 5 | { 6 | [ProtoContract] 7 | public class EmptyMessage 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Kadder.Maui/Platforms/Tizen/PlatformClass1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder.Maui 4 | { 5 | // All the code in this file is only included on Tizen. 6 | public class PlatformClass1 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Kadder/Utils/IObjectScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder.Utils 4 | { 5 | public interface IObjectScope : IDisposable 6 | { 7 | IObjectProvider Provider { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Messaging/EmptyMessageResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ProtoBuf; 3 | 4 | namespace Kadder.Messaging 5 | { 6 | [ProtoContract] 7 | public class EmptyMessageResult 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Kadder/Streaming/IAsyncResponseStream.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Kadder.Streaming 4 | { 5 | public interface IAsyncResponseStream : IAsyncStream where T : class 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Kadder/Utils/OperationType.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Utilies 2 | { 3 | public enum OperationType 4 | { 5 | Nothing, 6 | Add, 7 | Update, 8 | Delete 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Kadder/Streaming/IStreamReader.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Streaming 2 | { 3 | public interface IStreamReader where T : class 4 | { 5 | T Current { get; } 6 | 7 | void MoveNext(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Grpc/CallType.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Grpc 2 | { 3 | public enum CallType 4 | { 5 | Rpc = 0, 6 | ClientStreamRpc = 1, 7 | ServerStreamRpc = 2, 8 | DuplexStreamRpc = 3 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Simple/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | // appsettings.json 2 | { 3 | "GrpcServer": { 4 | "Options": { 5 | "PackageName": "Kadder.NoteServer", 6 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Streaming/IStreamWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Kadder.Streaming 4 | { 5 | public interface IStreamWriter where T : class 6 | { 7 | Task WriteAsync(T response); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Utils/IBinarySerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Utils 2 | { 3 | public interface IBinarySerializer 4 | { 5 | byte[] Serialize(T obj); 6 | 7 | T Deserialize(byte[] data); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/IGrpcServices.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Grpc.Core; 3 | 4 | namespace Kadder 5 | { 6 | public interface IGrpcServices 7 | { 8 | ServerServiceDefinition BindServices(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Kadder/Messaging/IMessagingContext.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Messaging 2 | { 3 | public interface IMessagingContext 4 | { } 5 | 6 | public interface IMessagingContext : IMessagingContext where T : class 7 | { 8 | void Request(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Simple/Duplex/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | // appsettings.json 2 | { 3 | "GrpcServer": { 4 | "Options": { 5 | "PackageName": "Kadder.NoteServer", 6 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 7 | }, 8 | "AssemblyNames": ["Protocol"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Kadder 2 | ## 关于 3 | Kadder 是一个简单无代码侵入的RPC框架,目前暂只支持 GRPC 。Kadder 依赖于动态创建代码技术,可以让代码RPC调用变得简单高效且不损失性能。 4 | 5 | 在服务端可以直接利用C#中的代码类来定义,无需编写Proto文件,降低GRPC门槛,甚至可以做到让你感受不到GRPC的存在。Kadder 建议采用 类库 Nuget包的方式来创建协议程序集,从而客户端只需要依赖协议程序集即可实现在客户端如同本地调用一般的方式来调用RPC。 6 | 7 | [] 8 | -------------------------------------------------------------------------------- /Kadder/Messaging/KServicer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder 4 | { 5 | [Obsolete("Please use KServicerAttribute or use KServicer suffix!(egg: KServicer -> NoteKServicer)")] 6 | public abstract class KServicer 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Kadder/Utils/IObjectProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder.Utils 4 | { 5 | public interface IObjectProvider 6 | { 7 | IObjectScope CreateScope(); 8 | 9 | T GetObject(); 10 | 11 | object GetObject(Type type); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Simple/Duplex/Protocol/INoteServicer.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Simple.Duplex.Protocol.Protocols; 2 | 3 | namespace Kadder.Simple.Duplex.Protocol; 4 | 5 | [KServicer] 6 | public interface INoteServicer 7 | { 8 | Task CreateAsync(CreateNoteRequest request); 9 | } 10 | -------------------------------------------------------------------------------- /Simple/Duplex/Client/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "GrpcClient": { 3 | "ProxyerOptions": [ 4 | { 5 | "Name": "Kadder.NoteClient", 6 | "PackageName": "Kadder.NoteServer", 7 | "AssemblyNames": ["Protocol"], 8 | "Addresses": [{"Address": "127.0.0.1:3002"}] 9 | }] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Kadder/Utils/IMessagingServicer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder.Utilies 4 | { 5 | [Obsolete("Please use KServicerAttribute or use KServicer suffix!(egg: KServicer -> NoteKServicer)")] 6 | public interface IMessagingServicer 7 | { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Simple/Server/Protocols/CreateNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace Kadder.Simple.Server.Protocols; 4 | 5 | [ProtoContract(ImplicitFields=ImplicitFields.AllPublic)] 6 | public class CreateNoteRequest 7 | { 8 | public string Title { get; set; } 9 | 10 | public string Content { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /Simple/Server/Protocols/CreateNoteResponse.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace Kadder.Simple.Server.Protocols; 4 | 5 | [ProtoContract(ImplicitFields=ImplicitFields.AllPublic)] 6 | public class CreateNoteResponse 7 | { 8 | public string Title { get; set; } 9 | 10 | public bool StatusCode { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /Kadder/Messaging/NoGRPCMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Kadder 4 | { 5 | public class NotGrpcMethodAttribute:Attribute 6 | { 7 | } 8 | 9 | public class NotGrpcServicerAttribute:Attribute 10 | { 11 | } 12 | 13 | public class NonKMethodAttribute:Attribute 14 | {} 15 | } 16 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/AsyncResponseStream.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using Kadder.Streaming; 3 | 4 | namespace Kadder.Grpc.Client 5 | { 6 | public class AsyncResponseStream : IAsyncResponseStream where TResponse : class 7 | { 8 | internal IAsyncStreamReader StreamReader { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Simple/Duplex/Protocol/Protocols/CreateNoteResponse.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace Kadder.Simple.Duplex.Protocol.Protocols; 4 | 5 | [ProtoContract] 6 | public class CreateNoteResponse 7 | { 8 | [ProtoMember(1)] 9 | public string Title { get; set; } = null!; 10 | 11 | [ProtoMember(2)] 12 | public bool Status { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /Simple/Duplex/Protocol/Protocols/CreateNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using ProtoBuf; 2 | 3 | namespace Kadder.Simple.Duplex.Protocol.Protocols; 4 | 5 | [ProtoContract] 6 | public class CreateNoteRequest 7 | { 8 | [ProtoMember(1)] 9 | public string Title { get; set; } = null!; 10 | 11 | [ProtoMember(2)] 12 | public string Content { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /Kadder/GrpcMethodCheck.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Kadder 4 | { 5 | public class GrpcMethodCheck 6 | { 7 | public void CheckRegService(MethodInfo method) 8 | { 9 | 10 | } 11 | 12 | private void CheckMethod(MethodInfo method) 13 | { 14 | // if(method.ReturnType.) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/ClientStreamResult.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Kadder.Streaming; 3 | 4 | namespace Kadder.Grpc.Client 5 | { 6 | public class ClientStreamResult where TRequest:class 7 | { 8 | public Task ResponseAsync{get; internal set;} 9 | 10 | public IAsyncRequestStream RequestStream{get; internal set;} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/AsyncRequestStream.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | using Kadder.Streaming; 3 | 4 | namespace Kadder.Grpc.Client 5 | { 6 | public class AsyncRequestStream : IAsyncRequestStream where TRequest : class 7 | { 8 | public AsyncRequestStream() 9 | { 10 | } 11 | 12 | internal IClientStreamWriter StreamWriter { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/DuplexStreamResult.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Streaming; 2 | 3 | namespace Kadder.Grpc.Client 4 | { 5 | public class DuplexStreamResult where TRequest : class where TResponse : class 6 | { 7 | public IAsyncRequestStream RequestStream { get; internal set; } 8 | 9 | public IAsyncResponseStream ResponseStream { get; internal set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Kadder/Utils/IJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Kadder.Utilies 8 | { 9 | public interface IJsonSerializer 10 | { 11 | string Serialize(object obj); 12 | 13 | T Deserialize(string jsonStr); 14 | 15 | object Deserialize(string jsonStr); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Kadder/KadderBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Kadder.Utils; 5 | 6 | namespace Kadder 7 | { 8 | public class KadderBuilder 9 | { 10 | private readonly List _assembles; 11 | 12 | public KadderBuilder() 13 | { 14 | _assembles = new List(); 15 | } 16 | 17 | public List Assemblies => _assembles; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Simple/Duplex/Server/NoteServicer.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Simple.Duplex.Protocol; 2 | using Kadder.Simple.Duplex.Protocol.Protocols; 3 | 4 | namespace Kadder.Simple.Duplex.Server; 5 | 6 | public class NoteServicer : INoteServicer 7 | { 8 | public Task CreateAsync(CreateNoteRequest request) 9 | { 10 | return Task.FromResult(new CreateNoteResponse() 11 | { 12 | Title = request.Title, 13 | Status = true 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Simple/Server/NoteServicer.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Simple.Server.Protocols; 2 | 3 | namespace Kadder.Simple.Server; 4 | 5 | [KServicer] 6 | public class NoteServicer 7 | { 8 | public Task CreateNoteAsync(CreateNoteRequest request) 9 | { 10 | Console.WriteLine($"Receive create note request[Title: {request.Title} -> Content: {request.Content}]"); 11 | 12 | return Task.FromResult(new CreateNoteResponse() { Title = request.Title, StatusCode = true }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/AsyncRequestStream.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Streaming; 2 | using Grpc.Core; 3 | 4 | namespace Kadder.Grpc.Server 5 | { 6 | public class AsyncRequestStream : IAsyncRequestStream where T : class 7 | { 8 | private readonly IAsyncStreamReader _reader; 9 | 10 | public AsyncRequestStream(IAsyncStreamReader reader) 11 | { 12 | _reader = reader; 13 | } 14 | 15 | public IAsyncStreamReader GrpcReader => _reader; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/Options/GrpcChannelOptions.cs: -------------------------------------------------------------------------------- 1 | using Grpc.Core; 2 | 3 | namespace Kadder.Grpc.Client.Options 4 | { 5 | public class GrpcChannelOptions 6 | { 7 | public GrpcChannelOptions() 8 | { 9 | Name = Address; 10 | Credentials = ChannelCredentials.Insecure; 11 | } 12 | 13 | public string Name { get; set; } 14 | 15 | public string Address { get; set; } 16 | 17 | public ChannelCredentials Credentials { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/AsyncResponseStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Grpc.Core; 3 | using Kadder.Streaming; 4 | 5 | namespace Kadder.Grpc.Server 6 | { 7 | public class AsyncResponseStream : IAsyncResponseStream where T : class 8 | { 9 | private readonly IServerStreamWriter _writer; 10 | 11 | public AsyncResponseStream(IServerStreamWriter writer) 12 | { 13 | _writer = writer; 14 | } 15 | 16 | public IServerStreamWriter GrpcWriter => _writer; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Kadder.Grpc.Client.Options; 4 | 5 | namespace Kadder.Grpc.Client 6 | { 7 | public class Client 8 | { 9 | public Client(List proxyers, List proxyerOptions) 10 | { 11 | Proxyers = proxyers; 12 | ProxyerOptions = proxyerOptions; 13 | } 14 | 15 | public List ProxyerOptions { get; } 16 | 17 | public List Proxyers { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /Simple/Duplex/Protocol/Protocol.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | net6.0 16 | enable 17 | enable 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/StreamMessage.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Streaming; 2 | 3 | namespace Kadder.Grpc.Client 4 | { 5 | public class StreamMessage 6 | { 7 | public static IAsyncRequestStream CreateRequest() where TRequest : class 8 | { 9 | var stream = new AsyncRequestStream(); 10 | return stream; 11 | } 12 | 13 | public static IAsyncResponseStream CreateResponse() where TResponse : class 14 | { 15 | return new AsyncResponseStream(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Kadder/Utils/NullJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Utilies 2 | { 3 | public class NullJsonSerializer : IJsonSerializer 4 | { 5 | public T Deserialize(string jsonStr) 6 | { 7 | throw new System.NotImplementedException(); 8 | } 9 | 10 | public object Deserialize(string jsonStr) 11 | { 12 | throw new System.NotImplementedException(); 13 | } 14 | 15 | public string Serialize(object obj) 16 | { 17 | throw new System.NotImplementedException(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Kadder/Utils/NewtonsoftJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Kadder.Utilies 4 | { 5 | public class NewtonsoftJsonSerializer:IJsonSerializer 6 | { 7 | public T Deserialize(string jsonStr) 8 | { 9 | return JsonConvert.DeserializeObject(jsonStr); 10 | } 11 | 12 | public object Deserialize(string jsonStr) 13 | { 14 | return JsonConvert.DeserializeObject(jsonStr); 15 | } 16 | 17 | public string Serialize(object obj) 18 | { 19 | return JsonConvert.SerializeObject(obj); 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Kadder/Utils/ObjectScope.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Utils; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Kadder.Utils 5 | { 6 | public class ObjectScope : IObjectScope 7 | { 8 | private readonly IServiceScope _scope; 9 | private readonly IObjectProvider _provider; 10 | 11 | public ObjectScope(IServiceScope scope) 12 | { 13 | _scope = scope; 14 | _provider = new ObjectProvider(scope.ServiceProvider); 15 | } 16 | 17 | public IObjectProvider Provider => _provider; 18 | 19 | public void Dispose() => _scope.Dispose(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Simple/Server/Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Exe 14 | net6.0 15 | enable 16 | enable 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Kadder/Utils/ObjectProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Kadder.Utils 5 | { 6 | public class ObjectProvider : IObjectProvider 7 | { 8 | private readonly IServiceProvider _serviceProvider; 9 | 10 | public ObjectProvider(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; 11 | 12 | public IObjectScope CreateScope() => new ObjectScope(_serviceProvider.CreateScope()); 13 | 14 | public T GetObject() => _serviceProvider.GetService(); 15 | 16 | public object GetObject(Type type) => _serviceProvider.GetService(type); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Simple/Duplex/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Simple.Duplex.Protocol; 2 | using Kadder.Simple.Duplex.Server; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | var host = Host.CreateDefaultBuilder() 8 | .ConfigureLogging((context, builder) => 9 | { 10 | builder.AddConsole(); 11 | builder.AddDebug(); 12 | builder.SetMinimumLevel(LogLevel.Debug); 13 | }) 14 | .UseGrpcServer((context,services,builder)=> 15 | { 16 | services.AddScoped(); 17 | }) 18 | .Build(); 19 | 20 | host.StartGrpcServer().Run(); 21 | -------------------------------------------------------------------------------- /Simple/Duplex/Client/Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Exe 15 | net6.0 16 | enable 17 | enable 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Simple/Duplex/Server/Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Exe 15 | net6.0 16 | enable 17 | enable 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Kadder.Test/Grpc/HelperTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xunit; 3 | using Kadder.Grpc; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | 7 | namespace Kadder.Test.Grpc 8 | { 9 | public class HelperTest 10 | { 11 | [Fact] 12 | public void ParseMethodReturnParameter_Test() 13 | { 14 | var taskGenericReturnMethod = typeof(TestData).GetMethod("TestReturnType"); 15 | var type=taskGenericReturnMethod.ParseMethodReturnParameter(); 16 | } 17 | 18 | internal class TestData 19 | { 20 | public Task TestReturnType(int p1) 21 | { 22 | throw new System.NotImplementedException(); 23 | } 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Kadder.Maui/MauiHostExtension.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Grpc.Client; 2 | 3 | namespace Kadder.Maui; 4 | 5 | public static class MauiHostExtension 6 | { 7 | public static MauiAppBuilder AddGrpcClient(this MauiAppBuilder hostBuilder, 8 | Action? builderAction = null, 9 | string configurationKeyName = "GrpcClient") 10 | { 11 | hostBuilder.Services.AddGrpcClient(hostBuilder.Configuration, (clientBuilder, configuration, services) => 12 | { 13 | builderAction?.Invoke(hostBuilder, clientBuilder); 14 | }); 15 | return hostBuilder; 16 | } 17 | 18 | public static MauiApp UseGrpcClient(this MauiApp app) 19 | { 20 | app.Services.UseGrpcClient(); 21 | return app; 22 | } 23 | } -------------------------------------------------------------------------------- /Simple/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using System.Reflection; 5 | using Kadder.Utils; 6 | 7 | var assemblies = new List(); 8 | assemblies.Add(Assembly.LoadFile("/Users/binwan/Documents/binwan-dev/Kadder/Simple/Server/temp/Server.dll")); 9 | var servicerTypes=ServicerHelper.GetServicerTypes(assemblies); 10 | foreach(var s in servicerTypes) 11 | Console.WriteLine(s.FullName); 12 | 13 | var host = Host.CreateDefaultBuilder() 14 | .ConfigureLogging((context,builder)=> 15 | { 16 | builder.SetMinimumLevel(LogLevel.Debug); 17 | builder.AddConsole(); 18 | builder.Services.AddLogging(); 19 | }) 20 | .UseGrpcServer() 21 | .Build(); 22 | 23 | host.StartGrpcServer().Run(); 24 | 25 | Console.WriteLine("Hello, World!"); 26 | -------------------------------------------------------------------------------- /Kadder/Kadder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 1.0.24 6 | 1.0.24 7 | true 8 | 12 9 | CS0618 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Simple/Duplex/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using Kadder.Simple.Duplex.Protocol; 2 | using Kadder.Simple.Duplex.Protocol.Protocols; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | var host = Host.CreateDefaultBuilder() 8 | .ConfigureLogging((context, builder) => 9 | { 10 | builder.AddConsole(); 11 | builder.SetMinimumLevel(LogLevel.Debug); 12 | }) 13 | .UseGrpcClient() 14 | .Build(); 15 | 16 | var servicer = host.Services.GetService() ?? throw new ArgumentNullException(nameof(INoteServicer)); 17 | var response = await servicer.CreateAsync(new CreateNoteRequest() 18 | { 19 | Title = "Note title", 20 | Content = "Note content" 21 | }); 22 | // See https://aka.ms/new-console-template for more information 23 | var status = response.Status ? "success" : "failed"; 24 | Console.WriteLine($"Create note {status}, title: {response.Title}"); 25 | -------------------------------------------------------------------------------- /Kadder.Test/Kadder.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Kadder/Utils/Message.cs: -------------------------------------------------------------------------------- 1 | namespace Kadder.Utilies 2 | { 3 | public class BaseMessage 4 | { 5 | private MessageExecutingType ExecutingType { get; set; } 6 | 7 | private string TypeFullName { get; set; } 8 | 9 | public void SetTypeFullName(string typeFullName) 10 | { 11 | TypeFullName = typeFullName; 12 | } 13 | 14 | public string GetTypeFullName() 15 | { 16 | if(string.IsNullOrWhiteSpace(TypeFullName)) 17 | { 18 | TypeFullName=this.GetType().FullName; 19 | } 20 | return TypeFullName; 21 | } 22 | 23 | public void SetMessageExecutingType(MessageExecutingType executingType) 24 | { 25 | ExecutingType = executingType; 26 | } 27 | 28 | public MessageExecutingType GetMessageExecutingType()=>ExecutingType; 29 | } 30 | 31 | public class MessageResult 32 | { 33 | 34 | } 35 | 36 | public enum MessageExecutingType 37 | { 38 | Command, 39 | Query 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Simple/Server/Server.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server.csproj", "{8F1916E8-6CDC-4604-BC58-2862231CAAD1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(SolutionProperties) = preSolution 14 | HideSolutionNode = FALSE 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {8F1916E8-6CDC-4604-BC58-2862231CAAD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {8F1916E8-6CDC-4604-BC58-2862231CAAD1}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {8F1916E8-6CDC-4604-BC58-2862231CAAD1}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {8F1916E8-6CDC-4604-BC58-2862231CAAD1}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Atlantis-Org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Kadder/Utils/RefelectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Linq; 5 | 6 | namespace Kadder.Utilies 7 | { 8 | public class RefelectionHelper 9 | { 10 | public static IList GetImplInterfaceTypes( 11 | Type type, bool interfaceFilter = false, params Assembly[] assemblys) 12 | { 13 | var types = new List(); 14 | foreach (var assembly in assemblys) 15 | { 16 | if (!interfaceFilter) 17 | { 18 | types.AddRange( 19 | assembly.GetModules()[0].GetTypes() 20 | .Where(p => p.GetInterface(type.Name) != null)); 21 | } 22 | else 23 | { 24 | types.AddRange( 25 | assembly.GetModules()[0].GetTypes() 26 | .Where(p => 27 | p.GetInterface(type.Name) != null && 28 | p.IsInterface)); 29 | } 30 | } 31 | return types; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Kadder.Simple.Client", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/Kadder.Simple.Client/bin/Debug/netcoreapp3.1/Kadder.Simple.Client.dll", 13 | "args": [], 14 | "cwd": "${workspaceFolder}/Kadder.Simple.Client", 15 | "console": "internalConsole", 16 | "stopAtEntry": false 17 | }, 18 | { 19 | "name": "Kadder.Simple.Server", 20 | "type": "coreclr", 21 | "request": "launch", 22 | "preLaunchTask": "build", 23 | "program": "${workspaceFolder}/Kadder.Simple.Server/bin/Debug/netcoreapp3.1/Kadder.Simple.Server.dll", 24 | "args": [], 25 | "cwd": "${workspaceFolder}/Kadder.Simple.Server", 26 | "console": "internalConsole", 27 | "stopAtEntry": false 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /Kadder/Grpc/Server/GrpcServerOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Grpc.Core; 4 | 5 | namespace Kadder 6 | { 7 | public class GrpcServerOptions 8 | { 9 | public GrpcServerOptions() 10 | { 11 | PackageName = string.Empty; 12 | IsGeneralProtoFile = true; 13 | ChannelOptions = new List(); 14 | Ports = new List(); 15 | } 16 | 17 | public string PackageName { get; set; } 18 | 19 | public bool IsGeneralProtoFile { get; set; } 20 | 21 | public List ChannelOptions { get; } 22 | 23 | public List Ports { get; } 24 | 25 | } 26 | 27 | public class GrpcServerPort 28 | { 29 | public GrpcServerPort() 30 | { 31 | Name = string.Empty; 32 | Host = "0.0.0.0"; 33 | Port = 1666; 34 | Credentials = ServerCredentials.Insecure; 35 | } 36 | 37 | public string Name { get; set; } 38 | 39 | public string Host { get; set; } 40 | 41 | public int Port { get; set; } 42 | 43 | public ServerCredentials Credentials { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Kadder/Utils/ServicerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Kadder.Utilies; 6 | 7 | namespace Kadder.Utils 8 | { 9 | public static class ServicerHelper 10 | { 11 | public static List GetServicerTypes(List assemblies) 12 | { 13 | var kServicers = new List(); 14 | foreach (var assembly in assemblies) 15 | { 16 | var types = assembly.GetModules()[0].GetTypes(); 17 | kServicers.AddRange( 18 | types.Where(p => (p.GetInterface(typeof(IMessagingServicer).Name) != null && p.IsInterface) || 19 | p.IsSubclassOf(typeof(KServicer)) || 20 | p.IsAssignableFrom(typeof(KServicer)) || 21 | p.Name.EndsWith("KServicer") || 22 | p.CustomAttributes.Count(x => x.AttributeType.FullName == typeof(KServicerAttribute).FullName) > 0)); 23 | } 24 | return kServicers; 25 | 26 | } 27 | 28 | public static MethodInfo[] GetMethod(Type servicerType) 29 | { 30 | return servicerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/StreamExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Kadder.Grpc.Server; 5 | using Kadder.Streaming; 6 | 7 | namespace Kadder.Grpc.Server 8 | { 9 | public static class ServerStreamExtension 10 | { 11 | public static Task WriteAsync(this IAsyncResponseStream serverStream, T message) where T : class 12 | { 13 | if (!(serverStream is AsyncResponseStream grpcServerStream)) 14 | throw new InvalidCastException("The stream is not grpc stream!"); 15 | 16 | return grpcServerStream.GrpcWriter.WriteAsync(message); 17 | } 18 | 19 | public static T GetCurrent(this IAsyncRequestStream serverStream) where T : class 20 | { 21 | if (!(serverStream is AsyncRequestStream grpcServerStream)) 22 | throw new InvalidCastException("The stream is not grpc stream!"); 23 | 24 | return grpcServerStream.GrpcReader.Current; 25 | } 26 | 27 | public static Task MoveNextAsync(this IAsyncRequestStream serverStream, CancellationToken cancellationToken) where T : class 28 | { 29 | if (!(serverStream is AsyncRequestStream grpcServerStream)) 30 | throw new InvalidCastException("The stream is not grpc stream!"); 31 | 32 | return grpcServerStream.GrpcReader.MoveNext(cancellationToken); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/HostExtension.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: Registr grpc client for netcore project 3 | * @Author: Bin Wan 4 | * @Email: email@wanbin.tech 5 | */ 6 | using System; 7 | using Kadder.Grpc.Client; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace Microsoft.Extensions.Hosting; 11 | 12 | public static class KadderGrpcClientHostExtension 13 | { 14 | /// 15 | /// Register GrpcClient 16 | /// 17 | /// The host builder context 18 | /// The client options builder action 19 | /// The client optios key in appsetting.json 20 | /// 21 | public static IHostBuilder AddGrpcClient(this IHostBuilder hostBuilder, 22 | Action builderAction = null, 23 | string configurationKeyName = "GrpcClient") 24 | { 25 | hostBuilder.ConfigureServices((context, services) => 26 | { 27 | services.AddGrpcClient(context.Configuration, (clientBuilder, configuration, services) => 28 | { 29 | builderAction?.Invoke(context, services, clientBuilder); 30 | }); 31 | }); 32 | return hostBuilder; 33 | } 34 | 35 | public static IHost UseGrpcClient(this IHost host) 36 | { 37 | host.Services.UseGrpcClient(); 38 | return host; 39 | } 40 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kadder 2 | ============================= 3 | [![](https://api.travis-ci.org/felixwan-git/Kadder.svg?branch=master)](https://www.travis-ci.org/felixwan-git/Kadder) 4 | 5 | Kadder 致力于简化C# RPC 调用过程,目前支持 Grpc 协议。Kadder使用事先约定模式开发。 6 | 7 | ### Grpc模式 8 | 该模式下使用 Namespace 代替 Proto 中 Package,使用接口和类代理Proto接口和消息体,从而无需拷贝、书写Proto文件,以C#接口和结构体来书写。 9 | 以下即可声明一个Proto协议,Grpc接口。 10 | ``` 11 | namespace Kadder 12 | { 13 | public interface IPersonServicer:IMessageServicer 14 | { 15 | // 普通模式 16 | Task HelloAsync(Request request); 17 | 18 | // 无参普通模式 19 | Task HelloVoidAsync();// 可以支持无参; 20 | 21 | // 客户端流模式 22 | Task HelloClientStreamAsync(IAsyncRequestStream request); 23 | 24 | // 服务端流模式 25 | Task HelloServerStreamAsync(Request request, IAsyncResponseStream response); 26 | 27 | // 双向流模式 28 | Task HelloDuplexStreamAsync(IAsyncRequestStream request, IAsyncResponseStream response); 29 | } 30 | } 31 | ``` 32 | ## 客户端调用 33 | ``` 34 | namespace Client 35 | { 36 | public class Program 37 | { 38 | public static void main(string[] args) 39 | { 40 | // 直接通过 ioc 组件拿到service,直接调用即可。 41 | var service = serviceProvider.GetService(); 42 | service.HelloAsync(request); 43 | } 44 | } 45 | } 46 | ``` 47 | ## 安装 Install 48 | ``` 49 | dotnet add package Kadder 50 | ``` 51 | 52 | ## 使用 Use 53 | 54 | 请查看 Simple 案例 或查看说明。 55 | -------------------------------------------------------------------------------- /docs/server.md: -------------------------------------------------------------------------------- 1 | # 仅编写服务端 2 | 3 | 1. 创建项目 4 | 5 | ```sh 6 | dotnet new console 7 | ``` 8 | 2. 添加Kadder 9 | 10 | ```sh 11 | dotnet add package Kadder 12 | ``` 13 | 14 | 3. 创建并声明服务类. 15 | 16 | ```csharp 17 | public record CreateMessage(string Title,string Content); 18 | public record CreateMessageResult(bool Status); 19 | 20 | [KServicer] 21 | public class NoteServicer 22 | { 23 | public Task CreateAsync(CreateMessage request) 24 | { 25 | return Task.FromResult(new CreateMessageResult(true)); 26 | } 27 | } 28 | ``` 29 | 30 | 4. 配置appsettings.json 31 | 32 | ```js 33 | // appsettings.json 34 | { 35 | "GrpcServer": { 36 | "Options": { 37 | "PackageName": "Kadder.NoteServer", 38 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 39 | } 40 | } 41 | } 42 | 43 | ``` 44 | 45 | 5. 注册NoteServicer服务并启动GrpcServer 46 | 47 | ```csharp 48 | Console.WriteLine("dd"); 49 | 50 | IHostBuilder builder = Host.CreateDefaultBuilder(); 51 | builder.UseGrpcServer(); 52 | 53 | IHost host = builder.Build(); 54 | host.StartGrpcServer(); 55 | ``` 56 | 57 | 58 | # 生成Proto文件 59 | 60 | 1. 安装 proto2csharp 工具 61 | 62 | ```sh 63 | dotnet tool install --global proto2csharp 64 | ``` 65 | 66 | 2. 运行命令 67 | 68 | ```sh 69 | proto2csharp proto -i ./NoteServer/ -o ./notesrv.proto -p notesrv 70 | # proto2csharp -i -o -p 71 | ``` -------------------------------------------------------------------------------- /Kadder/Utils/ProtobufBinarySerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Extensions.Logging; 4 | using Newtonsoft.Json; 5 | using ProtoBuf; 6 | 7 | namespace Kadder.Utils 8 | { 9 | public class ProtobufBinarySerializer : IBinarySerializer 10 | { 11 | private readonly ILogger _log; 12 | 13 | public ProtobufBinarySerializer(ILogger log) 14 | { 15 | _log = log; 16 | } 17 | 18 | public T Deserialize(byte[] data) 19 | { 20 | try 21 | { 22 | using (var memoryStream = new MemoryStream(data)) 23 | { 24 | return Serializer.Deserialize(memoryStream); 25 | } 26 | } 27 | catch (Exception ex) 28 | { 29 | _log.LogError(ex, $"Serialize failed! MsgName[{typeof(T).FullName}] Data[{JsonConvert.SerializeObject(data)}]"); 30 | throw ex; 31 | } 32 | } 33 | 34 | public byte[] Serialize(T obj) 35 | { 36 | try 37 | { 38 | using (var memoryStream = new MemoryStream()) 39 | { 40 | Serializer.Serialize(memoryStream, obj); 41 | return memoryStream.ToArray(); 42 | } 43 | } 44 | catch (Exception ex) 45 | { 46 | _log.LogError(ex, $"Serialize failed! MsgName[{typeof(T).FullName}] Data[{JsonConvert.SerializeObject(obj)}]"); 47 | throw ex; 48 | } 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /docs/server.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: toc:nil 2 | ** 仅编写服务端 3 | 1. 创建项目 4 | #+BEGIN_SRC sh 5 | dotnet new console 6 | #+END_SRC 7 | 2. 添加Kadder 8 | 9 | #+BEGIN_SRC sh 10 | dotnet add package Kadder 11 | #+END_SRC 12 | 13 | 3. 创建并声明服务类. 14 | 15 | #+BEGIN_SRC csharp 16 | public record CreateMessage(string Title,string Content); 17 | public record CreateMessageResult(bool Status); 18 | 19 | [KServicer] 20 | public class NoteServicer 21 | { 22 | public Task CreateAsync(CreateMessage request) 23 | { 24 | return Task.FromResult(new CreateMessageResult(true)); 25 | } 26 | } 27 | #+END_SRC 28 | 29 | 4. 配置appsettings.json 30 | 31 | #+BEGIN_SRC js 32 | // appsettings.json 33 | { 34 | "GrpcServer": { 35 | "Options": { 36 | "PackageName": "Kadder.NoteServer", 37 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 38 | } 39 | } 40 | } 41 | 42 | #+END_SRC 43 | 44 | 5. 注册NoteServicer服务并启动GrpcServer 45 | 46 | #+BEGIN_SRC csharp 47 | Console.WriteLine("dd"); 48 | 49 | IHostBuilder builder = Host.CreateDefaultBuilder(); 50 | builder.UseGrpcServer(); 51 | 52 | IHost host = builder.Build(); 53 | host.StartGrpcServer(); 54 | #+END_SRC 55 | 56 | ** 生成Proto文件 57 | 1. 安装 proto2csharp 工具 58 | 59 | #+BEGIN_SRC sh 60 | dotnet tool install --global proto2csharp 61 | #+END_SRC 62 | 63 | 2. 运行命令 64 | 65 | #+BEGIN_SRC sh 66 | proto2csharp proto -i ./NoteServer/ -o ./notesrv.proto -p notesrv 67 | # proto2csharp -i -o -p 68 | #+END_SRC 69 | 70 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/Options/GrpcClientOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Grpc.Core.Interceptors; 5 | 6 | namespace Kadder.Grpc.Client.Options 7 | { 8 | public class GrpcProxyerOptions 9 | { 10 | public GrpcProxyerOptions() 11 | { 12 | Name = string.Empty; 13 | PackageName = string.Empty; 14 | Addresses = new List(); 15 | Assemblies = new List(); 16 | Interceptors = new List(); 17 | AssemblyNames = new List(); 18 | ConnectSecondTimeout = 10; 19 | KeepLive = true; 20 | } 21 | 22 | public string Name { get; set; } 23 | 24 | public string PackageName { get; set; } 25 | 26 | public IList Addresses { get; set; } 27 | 28 | /// 29 | /// Connection timeout (unit: s) 30 | /// 31 | public int ConnectSecondTimeout { get; set; } 32 | 33 | /// 34 | /// Keep connect live 35 | /// 36 | public bool KeepLive { get; set; } 37 | 38 | public List Assemblies { get; set; } 39 | 40 | public List Interceptors { get; set; } 41 | 42 | public List AssemblyNames { get; set; } 43 | 44 | public GrpcProxyerOptions AddAssembly(params Assembly[] assemblies) 45 | { 46 | Assemblies.AddRange(assemblies); 47 | return this; 48 | } 49 | 50 | public GrpcProxyerOptions AddInterceptor() 51 | { 52 | Interceptors.Add(typeof(Interceptor)); 53 | return this; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Kadder/GrpcOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Kadder.Utilies; 6 | 7 | namespace Kadder 8 | { 9 | public class GrpcOptions 10 | { 11 | public GrpcOptions() 12 | { 13 | ScanAssemblies = new string[] 14 | { 15 | Assembly.GetEntryAssembly().FullName 16 | }; 17 | } 18 | 19 | public string Host { get; set; } 20 | 21 | public int Port { get; set; } 22 | 23 | public string NamespaceName { get; set; } 24 | 25 | public string ServiceName { get; set; } 26 | 27 | public string[] ScanAssemblies { get; set; } 28 | 29 | public Assembly[] GetScanAssemblies() 30 | { 31 | var assemblies = new List(); 32 | foreach (var item in ScanAssemblies) 33 | { 34 | assemblies.Add(Assembly.Load(item)); 35 | } 36 | return assemblies.ToArray(); 37 | } 38 | 39 | public Type[] GetKServicers() 40 | { 41 | var kServicers = new List(); 42 | foreach (var assembly in GetScanAssemblies()) 43 | { 44 | var types = assembly.GetModules()[0].GetTypes(); 45 | kServicers.AddRange( 46 | types.Where(p => p.GetInterface(typeof(IMessagingServicer).Name) !=null && p.IsInterface || 47 | p.IsSubclassOf(typeof(KServicer)) || 48 | p.IsAssignableFrom(typeof(KServicer)) || 49 | p.Name.EndsWith("KServicer") || 50 | p.CustomAttributes.Count(x => x.AttributeType == typeof(KServicerAttribute)) > 0)); 51 | } 52 | return kServicers.ToArray(); 53 | } 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Kadder.Simple.Client/Kadder.Simple.Client.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "build-server", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "build", 22 | "${workspaceFolder}/Kadder.Simple.Server/Kadder.Simple.Server.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "publish", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "publish", 34 | "${workspaceFolder}/Kadder.Simple.Client/Kadder.Simple.Client.csproj", 35 | "/property:GenerateFullPaths=true", 36 | "/consoleloggerparameters:NoSummary" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | }, 40 | { 41 | "label": "watch", 42 | "command": "dotnet", 43 | "type": "process", 44 | "args": [ 45 | "watch", 46 | "run", 47 | "${workspaceFolder}/Kadder.Simple.Client/Kadder.Simple.Client.csproj", 48 | "/property:GenerateFullPaths=true", 49 | "/consoleloggerparameters:NoSummary" 50 | ], 51 | "problemMatcher": "$msCompile" 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /Kadder/Grpc/Client/Extension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Grpc.Core; 5 | using Kadder.Grpc.Client; 6 | using Kadder.Streaming; 7 | using Kadder.Utilies; 8 | using Kadder.Utils; 9 | 10 | namespace Kadder.Grpc.Client 11 | { 12 | public static class KadderGrpcClientExtension 13 | { 14 | public static Task WriteAsync(this IAsyncRequestStream requestStream, T message) where T : class 15 | { 16 | if (!(requestStream is AsyncRequestStream grpcRequestStream)) 17 | throw new InvalidCastException("The stream is not grpc stream!"); 18 | 19 | return grpcRequestStream.StreamWriter.WriteAsync(message); 20 | } 21 | 22 | public static Task CompleteAsync(this IAsyncRequestStream requestStream) where T : class 23 | { 24 | if (!(requestStream is AsyncRequestStream grpcRequestStream)) 25 | throw new InvalidCastException("The stream is not grpc stream!"); 26 | 27 | return grpcRequestStream.StreamWriter.CompleteAsync(); 28 | } 29 | 30 | public static T GetCurrent(this IAsyncResponseStream responseStream) where T : class 31 | { 32 | if (!(responseStream is AsyncResponseStream grpcResponseStream)) 33 | throw new InvalidCastException("The stream is not grpc stream!"); 34 | 35 | return grpcResponseStream.StreamReader.Current; 36 | } 37 | 38 | public static Task MoveNextAsync(this IAsyncResponseStream responseStream, CancellationToken cancellationToken) where T : class 39 | { 40 | if (!(responseStream is AsyncResponseStream grpcResponseStream)) 41 | throw new InvalidCastException("The stream is not grpc stream!"); 42 | 43 | return grpcResponseStream.StreamReader.MoveNext(cancellationToken); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Simple/Duplex/Duplex.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{8404002D-0296-4982-BEBF-7E4EE2E1EF16}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Protocol", "Protocol\Protocol.csproj", "{FAECA047-5305-4549-ABC6-923B721F937E}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{5CF19DB0-AD24-4F83-B139-5F9996269C4A}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {8404002D-0296-4982-BEBF-7E4EE2E1EF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {8404002D-0296-4982-BEBF-7E4EE2E1EF16}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {8404002D-0296-4982-BEBF-7E4EE2E1EF16}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {8404002D-0296-4982-BEBF-7E4EE2E1EF16}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {FAECA047-5305-4549-ABC6-923B721F937E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {FAECA047-5305-4549-ABC6-923B721F937E}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {FAECA047-5305-4549-ABC6-923B721F937E}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {FAECA047-5305-4549-ABC6-923B721F937E}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {5CF19DB0-AD24-4F83-B139-5F9996269C4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {5CF19DB0-AD24-4F83-B139-5F9996269C4A}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {5CF19DB0-AD24-4F83-B139-5F9996269C4A}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {5CF19DB0-AD24-4F83-B139-5F9996269C4A}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /Kadder.Maui/Kadder.Maui.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | net8.0-maccatalyst;net8.0-windows10.0.19041.0 6 | 7 | 8 | 9 | true 10 | true 11 | enable 12 | 1.0.0-beta.2405251046 13 | enable 14 | true 15 | 16 | 17 | 13.1 18 | 19 | 10.0.17763.0 20 | 10.0.17763.0 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Kadder/Utils/Bcl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Kadder 5 | { 6 | internal class Bcl 7 | { 8 | internal static string Proto=@"message TimeSpan { 9 | sint64 value = 1; // the size of the timespan (in units of the selected scale) 10 | TimeSpanScale scale = 2; // the scale of the timespan [default = DAYS] 11 | enum TimeSpanScale { 12 | DAYS = 0; 13 | HOURS = 1; 14 | MINUTES = 2; 15 | SECONDS = 3; 16 | MILLISECONDS = 4; 17 | TICKS = 5; 18 | 19 | MINMAX = 15; // dubious 20 | } 21 | } 22 | 23 | message DateTime { 24 | sint64 value = 1; // the offset (in units of the selected scale) from 1970/01/01 25 | TimeSpanScale scale = 2; // the scale of the timespan [default = DAYS] 26 | DateTimeKind kind = 3; // the kind of date/time being represented [default = UNSPECIFIED] 27 | enum TimeSpanScale { 28 | DAYS = 0; 29 | HOURS = 1; 30 | MINUTES = 2; 31 | SECONDS = 3; 32 | MILLISECONDS = 4; 33 | TICKS = 5; 34 | 35 | MINMAX = 15; // dubious 36 | } 37 | enum DateTimeKind 38 | { 39 | // The time represented is not specified as either local time or Coordinated Universal Time (UTC). 40 | UNSPECIFIED = 0; 41 | // The time represented is UTC. 42 | UTC = 1; 43 | // The time represented is local time. 44 | LOCAL = 2; 45 | } 46 | } 47 | 48 | message NetObjectProxy { 49 | int32 existingObjectKey = 1; // for a tracked object, the key of the **first** time this object was seen 50 | int32 newObjectKey = 2; // for a tracked object, a **new** key, the first time this object is seen 51 | int32 existingTypeKey = 3; // for dynamic typing, the key of the **first** time this type was seen 52 | int32 newTypeKey = 4; // for dynamic typing, a **new** key, the first time this type is seen 53 | string typeName = 8; // for dynamic typing, the name of the type (only present along with newTypeKey) 54 | bytes payload = 10; // the new string/value (only present along with newObjectKey) 55 | } 56 | 57 | message Guid { 58 | fixed64 lo = 1; // the first 8 bytes of the guid (note:crazy-endian) 59 | fixed64 hi = 2; // the second 8 bytes of the guid (note:crazy-endian) 60 | } 61 | 62 | message Decimal { 63 | uint64 lo = 1; // the first 64 bits of the underlying value 64 | uint32 hi = 2; // the last 32 bis of the underlying value 65 | uint32 signScale = 3; // the number of decimal digits (bits 1-16), and the sign (bit 0) 66 | }"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/GrpcServerBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Kadder.Grpc.Server 6 | { 7 | public class GrpcServerBuilder : KadderBuilder 8 | { 9 | public GrpcServerBuilder() 10 | { 11 | GrpcServicerProxyers = new List(); 12 | Interceptors = new List(); 13 | Options = new GrpcServerOptions(); 14 | AssemblyNames = new List(); 15 | } 16 | 17 | public GrpcServerOptions Options { get; set; } 18 | 19 | public string CodeCacheDir { get; set; } = string.Empty; 20 | 21 | public string DllCacheDir { get; set; } = string.Empty; 22 | 23 | internal IList GrpcServicerProxyers { get; set; } 24 | 25 | internal List Interceptors { get; set; } 26 | 27 | public List AssemblyNames { get; set; } 28 | 29 | public GrpcServerBuilder AddInterceptor() 30 | { 31 | Interceptors.Add(typeof(Interceptor)); 32 | return this; 33 | } 34 | 35 | public override string ToString() 36 | { 37 | var str = new StringBuilder(); 38 | str.AppendLine("############# Grpc Server Options #############"); 39 | str.AppendLine("Options"); 40 | str.AppendLine($" PackageName: {Options.PackageName}"); 41 | str.AppendLine($" IsGeneralProtoFile: {Options.IsGeneralProtoFile}"); 42 | str.AppendLine(" ChannelOptions"); 43 | foreach(var channel in Options.ChannelOptions) 44 | str.AppendLine($" Name: {channel.Name}, Value: {channel.StringValue}"); 45 | str.AppendLine(" ListenPorts"); 46 | foreach(var port in Options.Ports) 47 | str.AppendLine($" Name: {port.Name}, Host: {port.Host}, Port: {port.Port}, Credentials: {port.Credentials.GetType().Name}"); 48 | 49 | str.AppendLine(); 50 | str.AppendLine("Assemblies:"); 51 | foreach(var assembly in Assemblies) 52 | str.AppendLine($" {assembly.FullName}"); 53 | 54 | str.AppendLine(); 55 | str.AppendLine("Interceptors:"); 56 | foreach (var interceptor in Interceptors) 57 | str.AppendLine($" {interceptor.Name}"); 58 | 59 | str.AppendLine(); 60 | str.AppendLine("############# Grpc Server Options #############"); 61 | 62 | return str.ToString(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Kadder.Test/Grpc/Client/ServicerProxyGeneratorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Kadder.Grpc.Client; 6 | using Xunit; 7 | 8 | namespace Kadder.Test.Grpc.Client 9 | { 10 | public class ServicerProxyGeneratorTest 11 | { 12 | [Fact] 13 | public void Generate_Test() 14 | { 15 | var generator = new ServicerProxyGenerator("Test",new List(){ typeof(TestData) }); 16 | var testDataClassDescripter = generator.Generate().FirstOrDefault(); 17 | Assert.NotNull(testDataClassDescripter); 18 | 19 | var notGrpcTaskGenericMethod=testDataClassDescripter.Methods.FirstOrDefault(p => p.Name == "NotGrpcTaskGenericMethod"); 20 | Assert.NotNull(notGrpcTaskGenericMethod); 21 | Assert.Equal(notGrpcTaskGenericMethod.ReturnTypeStr, "System.Threading.Tasks.Task"); 22 | Assert.Equal(notGrpcTaskGenericMethod.Parameters.Count, 3); 23 | 24 | var taskGenericMethod = testDataClassDescripter.Methods.FirstOrDefault(p => p.Name == "TaskGenericMethod"); 25 | Assert.NotNull(taskGenericMethod); 26 | Assert.Equal(taskGenericMethod.ReturnTypeStr, "Task"); 27 | Assert.Equal(taskGenericMethod.Parameters.Count, 1); 28 | 29 | var tupleNotGrpcMethod = testDataClassDescripter.Methods.FirstOrDefault(p => p.Name == "TupleNotGrpcMethod"); 30 | Assert.NotNull(tupleNotGrpcMethod); 31 | Assert.Equal(tupleNotGrpcMethod.ReturnTypeStr, "System.ValueTuple"); 32 | Assert.Equal(tupleNotGrpcMethod.Parameters.Count, 3); 33 | } 34 | 35 | internal class TestData 36 | { 37 | public Task TaskGenericMethod(ServicerProxyGeneratorTest test) 38 | { 39 | throw new System.NotImplementedException(); 40 | } 41 | 42 | [NotGrpcMethod] 43 | public Task NotGrpcTaskGenericMethod(int p1, int p2, string p3) 44 | { 45 | throw new System.NotImplementedException(); 46 | } 47 | 48 | [NotGrpcMethod] 49 | public (int a, string b, ServicerProxyGeneratorTest c) TupleNotGrpcMethod(int p1, int p2, ServicerProxyGeneratorTest c) 50 | { 51 | throw new System.NotImplementedException(); 52 | } 53 | } 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/ClientBuilder.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: Grpc client builder class 3 | * @Author: Bin Wan 4 | * @Email: email@wanbin.tech 5 | */ 6 | using System.Reflection; 7 | using System.Linq; 8 | using System; 9 | using System.Collections.Generic; 10 | using Kadder.Grpc.Client.Options; 11 | using Kadder.Utils; 12 | using Grpc.Core.Interceptors; 13 | using Microsoft.Extensions.Configuration; 14 | using Microsoft.Extensions.DependencyInjection; 15 | 16 | namespace Kadder.Grpc.Client 17 | { 18 | public class ClientBuilder : KadderBuilder 19 | { 20 | /// 21 | /// Default configuration key in appsetting.json file 22 | /// 23 | public const string ConfigurationKeyName = "GrpcClient"; 24 | 25 | public ClientBuilder() 26 | { 27 | ProxyerOptions = new List(); 28 | GlobalInterceptors = new List(); 29 | } 30 | 31 | public string CodeCacheDir { get; set; } = string.Empty; 32 | 33 | public string DllCacheDir { get; set; } = string.Empty; 34 | 35 | public List GlobalInterceptors { get; internal set; } 36 | 37 | public List ProxyerOptions { get; set; } 38 | 39 | /// 40 | /// Add new grpc client for servicers. 41 | /// 42 | /// grpc client options 43 | /// 44 | public ClientBuilder AddProxyer(GrpcProxyerOptions options) 45 | { 46 | ProxyerOptions.Add(options); 47 | 48 | return this; 49 | } 50 | 51 | public Client Build() 52 | { 53 | var proxyers = new List(); 54 | 55 | foreach (var proxyerOptions in ProxyerOptions) 56 | { 57 | foreach (var assemblyName in proxyerOptions.AssemblyNames) 58 | proxyerOptions.AddAssembly(Assembly.Load(assemblyName)); 59 | proxyerOptions.Interceptors.AddRange(GlobalInterceptors); 60 | 61 | var servicerType = ServicerHelper.GetServicerTypes(proxyerOptions.Assemblies); 62 | proxyers.Add(new GrpcProxyer(servicerType, proxyerOptions)); 63 | Assemblies.AddRange(proxyerOptions.Assemblies); 64 | } 65 | return new Client(proxyers, ProxyerOptions); 66 | } 67 | 68 | public ClientBuilder AddGlobalInterceptor() where TInterceptor : Interceptor 69 | { 70 | GlobalInterceptors.Add(typeof(TInterceptor)); 71 | return this; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /docs/duplex.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: toc:nil 2 | ** Duplex C#模式 3 | 该模式下推荐使用nuget包的方式来引用,即创建 Server、Client、Protocol三个项目,将rpc服务以及接口对象放入Protocol项目中并打包成nuget包,提供给Server、Client使用。 4 | 5 | *** Protocol 6 | 1. 创建项目并引用Kadder 7 | 8 | #+BEGIN_SRC sh 9 | dotnet new classlib 10 | dotnet add package Kadder 11 | #+END_SRC 12 | 2. 声明Servicer以及对象 13 | 14 | #+BEGIN_SRC csharp 15 | [ProtoContract] 16 | public class CreateNoteRequest 17 | { 18 | [ProtoMember(1)] 19 | public string Title{get;set;} 20 | [ProtoMember(2)] 21 | public string Content{get;set;} 22 | } 23 | 24 | [ProtoContract] 25 | public class CreateNoteResponse 26 | { 27 | [ProtoMember(1)] 28 | public string Title{get;set;} 29 | [ProtoMember(2)] 30 | public bool Status{get;set;} 31 | } 32 | 33 | [KServicer] 34 | public interface INoteServicer 35 | { 36 | Task CreateAsync(CreateNoteRequest request); 37 | } 38 | #+END_SRC 39 | *** Server 40 | 1. 创建项目 41 | 42 | #+BEGIN_SRC sh 43 | dotnet new console && \ 44 | dotnet add package Kadder &&\ 45 | # 此处是上一步Protocol的nuget包 46 | dotnet add package Protocol &&\ 47 | dotnet add Microsoft.Extensions.Hosting 48 | #+END_SRC 49 | 50 | 2. 实现Servicer 51 | 52 | #+BEGIN_SRC csharp 53 | public class NoteServicer:INoteServicer 54 | { 55 | public Task CreateAsync(CreateNoteRequest request) 56 | { 57 | return Task.FromResult(new CreateNoteResponse() 58 | { 59 | Title = request.Title, 60 | Status=true 61 | }); 62 | } 63 | } 64 | #+END_SRC 65 | 66 | 3. 注册服务 67 | 68 | #+BEGIN_SRC csharp 69 | var host = Host.CreateDefaultBuilder().UseGrpcServer((context,services,builder)=> 70 | { 71 | services.AddScoped(); 72 | }) 73 | .Build(); 74 | 75 | host.StartGrpcServer().Run(); 76 | #+END_SRC 77 | 78 | #+BEGIN_SRC json 79 | // appsettings.json 80 | { 81 | "GrpcServer": { 82 | "Options": { 83 | "PackageName": "Note.Server", 84 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 85 | }, 86 | "AssemblyNames": ["Protocol"] 87 | } 88 | } 89 | #+END_SRC 90 | 91 | *** Client 92 | 1. 创建项目 93 | 94 | #+BEGIN_SRC sh 95 | dotnet new console 96 | dotnet add package Kadder 97 | dotnet add package Protocol 98 | dotnet add Microsoft.Extensions.Hosting 99 | #+END_SRC 100 | 101 | 2. 注册并调用 102 | 103 | #+BEGIN_SRC csharp 104 | var host = Host.CreateDefaultBuilder() 105 | .UseGrpcClient() 106 | .Build(); 107 | 108 | var servicer = host.Services.GetService() ?? throw new ArgumentNullException(nameof(INoteServicer)); 109 | var response = await servicer.CreateAsync(new CreateNoteRequest() 110 | { 111 | Title = "Note title", 112 | Content = "Note content" 113 | }); 114 | #+END_SRC 115 | 116 | #+BEGIN_SRC json 117 | //appsettings.json 118 | { 119 | "GrpcClient": { 120 | "ProxyerOptions": [ 121 | { 122 | "Name": "Note.Client", 123 | "PackageName": "Note.Server", 124 | "AssemblyNames": ["Protocol"], 125 | "Addresses": [{"Address": "127.0.0.1:3002"}] 126 | }] 127 | } 128 | } 129 | #+END_SRC 130 | 131 | 132 | -------------------------------------------------------------------------------- /docs/duplex.md: -------------------------------------------------------------------------------- 1 | # Duplex C#模式 2 | 3 | 该模式下推荐使用nuget包的方式来引用,即创建 Server、Client、Protocol三个项目,将rpc服务以及接口对象放入Protocol项目中并打包成nuget包,提供给Server、Client使用。 4 | 5 | 6 | ## Protocol 7 | 8 | 1. 创建项目并引用Kadder 9 | 10 | ```sh 11 | dotnet new classlib 12 | dotnet add package Kadder 13 | ``` 14 | 2. 声明Servicer以及对象 15 | 16 | ```csharp 17 | [ProtoContract] 18 | public class CreateNoteRequest 19 | { 20 | [ProtoMember(1)] 21 | public string Title{get;set;} 22 | [ProtoMember(2)] 23 | public string Content{get;set;} 24 | } 25 | 26 | [ProtoContract] 27 | public class CreateNoteResponse 28 | { 29 | [ProtoMember(1)] 30 | public string Title{get;set;} 31 | [ProtoMember(2)] 32 | public bool Status{get;set;} 33 | } 34 | 35 | [KServicer] 36 | public interface INoteServicer 37 | { 38 | Task CreateAsync(CreateNoteRequest request); 39 | } 40 | ``` 41 | 42 | 43 | ## Server 44 | 45 | 1. 创建项目 46 | 47 | ```sh 48 | dotnet new console && \ 49 | dotnet add package Kadder &&\ 50 | # 此处是上一步Protocol的nuget包 51 | dotnet add package Protocol &&\ 52 | dotnet add Microsoft.Extensions.Hosting 53 | ``` 54 | 55 | 2. 实现Servicer 56 | 57 | ```csharp 58 | public class NoteServicer:INoteServicer 59 | { 60 | public Task CreateAsync(CreateNoteRequest request) 61 | { 62 | return Task.FromResult(new CreateNoteResponse() 63 | { 64 | Title = request.Title, 65 | Status=true 66 | }); 67 | } 68 | } 69 | ``` 70 | 71 | 3. 注册服务 72 | 73 | ```csharp 74 | var host = Host.CreateDefaultBuilder().UseGrpcServer((context,services,builder)=> 75 | { 76 | services.AddScoped(); 77 | }) 78 | .Build(); 79 | 80 | host.StartGrpcServer().Run(); 81 | ``` 82 | 83 | ```json 84 | // appsettings.json 85 | { 86 | "GrpcServer": { 87 | "Options": { 88 | "PackageName": "Note.Server", 89 | "Ports": [{ "Host": "0.0.0.0", "Port": 3002 }] 90 | }, 91 | "AssemblyNames": ["Protocol"] 92 | } 93 | } 94 | ``` 95 | 96 | 97 | ## Client 98 | 99 | 1. 创建项目 100 | 101 | ```sh 102 | dotnet new console 103 | dotnet add package Kadder 104 | dotnet add package Protocol 105 | dotnet add Microsoft.Extensions.Hosting 106 | ``` 107 | 108 | 2. 注册并调用 109 | 110 | ```csharp 111 | var host = Host.CreateDefaultBuilder() 112 | .UseGrpcClient() 113 | .Build(); 114 | 115 | var servicer = host.Services.GetService() ?? throw new ArgumentNullException(nameof(INoteServicer)); 116 | var response = await servicer.CreateAsync(new CreateNoteRequest() 117 | { 118 | Title = "Note title", 119 | Content = "Note content" 120 | }); 121 | ``` 122 | 123 | ```json 124 | //appsettings.json 125 | { 126 | "GrpcClient": { 127 | "ProxyerOptions": [ 128 | { 129 | "Name": "Note.Client", 130 | "PackageName": "Note.Server", 131 | "AssemblyNames": ["Protocol"], 132 | "Addresses": [{"Address": "127.0.0.1:3002"}] 133 | }] 134 | } 135 | } 136 | ``` -------------------------------------------------------------------------------- /Kadder/Grpc/Client/ServiceExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using GenAssembly; 5 | using Grpc.Core; 6 | using Kadder.Utils; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace Kadder.Grpc.Client; 11 | 12 | public static class ServiceExtension 13 | { 14 | public static IServiceCollection AddGrpcClient(this IServiceCollection services, 15 | IConfiguration configuration, 16 | Action clientBuilderAction=null, 17 | string configurationKeyName="GrpcClient") 18 | { 19 | var builder = configuration.GetSection(configurationKeyName).Get() ?? new ClientBuilder(); 20 | clientBuilderAction?.Invoke(builder, configuration, services); 21 | var client = builder.Build(); 22 | 23 | var codeBuilder = new CodeBuilder("CodeGenerate"); 24 | if (!string.IsNullOrWhiteSpace(builder.CodeCacheDir)) 25 | CodeBuilder.CodeCachePath = builder.CodeCacheDir; 26 | if (!string.IsNullOrWhiteSpace(builder.DllCacheDir)) 27 | CodeBuilder.DllCachePath = builder.DllCacheDir; 28 | 29 | foreach (var proxyer in client.Proxyers) 30 | { 31 | codeBuilder.CreateClass( 32 | new ServicerProxyGenerator(proxyer.Options.PackageName, proxyer.ServicerTypes.ToList()).Generate() 33 | .ToArray()); 34 | codeBuilder.AddAssemblyRefence(proxyer.Options.Assemblies.ToArray()); 35 | } 36 | 37 | codeBuilder.AddAssemblyRefence(Assembly.GetExecutingAssembly()) 38 | .AddAssemblyRefence(typeof(ServerServiceDefinition).Assembly) 39 | .AddAssemblyRefence(typeof(ServiceProviderServiceExtensions).Assembly) 40 | .AddAssemblyRefence(typeof(Console).Assembly) 41 | .AddAssemblyRefence(typeof(KadderBuilder).Assembly) 42 | .AddAssemblyRefence(typeof(GrpcServerOptions).Assembly) 43 | .AddAssemblyRefence(builder.GetType().Assembly); 44 | 45 | var codeAssembly = codeBuilder.BuildAsync().Result; 46 | foreach (var servicerProxyer in codeBuilder.Classes) 47 | { 48 | var namespaces = $"{servicerProxyer.Namespace}.{servicerProxyer.Name}"; 49 | var proxyerType = codeAssembly.Assembly.GetType(namespaces) ?? throw new InvalidOperationException("Cannot get CodeAssembly"); 50 | var servicerType = proxyerType?.GetInterfaces()?.FirstOrDefault(); 51 | if (servicerType == null) 52 | services.AddSingleton(proxyerType!); 53 | else 54 | services.AddSingleton(servicerType, proxyerType!); 55 | } 56 | 57 | foreach (var proxyerOptions in client.ProxyerOptions) 58 | foreach (var interceptor in proxyerOptions.Interceptors) 59 | services.AddSingleton(interceptor); 60 | 61 | services.AddSingleton(client); 62 | services.AddSingleton(); 63 | services.AddSingleton(); 64 | services.AddSingleton(); 65 | return services; 66 | } 67 | 68 | public static IServiceProvider UseGrpcClient(this IServiceProvider serviceProvider) 69 | { 70 | KadderOptions.KadderObjectProvider = serviceProvider.GetService(); 71 | return serviceProvider; 72 | } 73 | } -------------------------------------------------------------------------------- /Kadder/Grpc/Client/GrpcProxyer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Grpc.Core; 6 | using Grpc.Core.Interceptors; 7 | using Kadder.Grpc.Client.Options; 8 | using Kadder.Utils; 9 | 10 | namespace Kadder.Grpc.Client 11 | { 12 | public class GrpcProxyer 13 | { 14 | private static IDictionary _proxyerDict; 15 | 16 | static GrpcProxyer() 17 | { 18 | _proxyerDict = new Dictionary(); 19 | } 20 | 21 | private readonly List _servicerTypes; 22 | private readonly GrpcProxyerOptions _proxyerOptions; 23 | private readonly IDictionary _channels; 24 | 25 | public GrpcProxyer(List servicerTypes, GrpcProxyerOptions options) 26 | { 27 | _servicerTypes = servicerTypes; 28 | _proxyerOptions = options; 29 | _channels = new Dictionary(); 30 | 31 | foreach (var opt in options.Addresses) 32 | AddChannel(opt); 33 | 34 | foreach (var servicerType in servicerTypes) 35 | { 36 | var servicerName = servicerType.FullName; 37 | if (!string.IsNullOrWhiteSpace(options.PackageName)) 38 | servicerName = $"{options.PackageName}.{servicerType.Name}"; 39 | ProxyerDict.Add(servicerName, this); 40 | } 41 | } 42 | 43 | public static IDictionary ProxyerDict => _proxyerDict; 44 | 45 | public IReadOnlyList ServicerTypes => _servicerTypes; 46 | 47 | public GrpcProxyerOptions Options => _proxyerOptions; 48 | 49 | public virtual ChannelInfo GetChannel() 50 | => _channels.FirstOrDefault().Value; 51 | 52 | public void AddChannel(GrpcChannelOptions options) 53 | => _channels.Add(options.Address, setChannels(options)); 54 | 55 | 56 | public async Task RemoveChannelAsync(string address) 57 | { 58 | if (!_channels.TryGetValue(address, out ChannelInfo channel)) 59 | return; 60 | 61 | await channel.Channel.ShutdownAsync(); 62 | channel.Channel = null; 63 | } 64 | 65 | private ChannelInfo setChannels(GrpcChannelOptions options) 66 | { 67 | var channel = new ChannelInfo() 68 | { 69 | Channel = new Channel(options.Address, options.Credentials), 70 | Options = options, 71 | ProxyerOptions = _proxyerOptions 72 | }; 73 | return channel; 74 | } 75 | 76 | public class ChannelInfo 77 | { 78 | private CallInvoker _invoker; 79 | 80 | public Channel Channel { get; set; } 81 | 82 | public GrpcProxyerOptions ProxyerOptions { get; set; } 83 | 84 | public GrpcChannelOptions Options { get; set; } 85 | 86 | public CallInvoker GetInvoker(IObjectProvider provider) 87 | { 88 | if (_invoker == null) 89 | { 90 | _invoker = Channel.CreateCallInvoker(); 91 | foreach (var interceptor in ProxyerOptions.Interceptors) 92 | _invoker = _invoker.Intercept((Interceptor)provider.GetObject(interceptor)); 93 | } 94 | return _invoker; 95 | } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /Kadder/Utils/InvitationAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Kadder.Utilities 8 | { 9 | public class InvitationAlgorithm: SerialAlgorithm 10 | { 11 | public readonly static InvitationAlgorithm Instance = new InvitationAlgorithm(); 12 | 13 | private InvitationAlgorithm() : 14 | base(new char[] { 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', '1', '2', '3' }, 15 | new char[] { '9', '0', '4', 'A', 'Z', '5', 'Y', '6', '7', 'B', 'C', '8' }, 6) 16 | { 17 | 18 | } 19 | } 20 | 21 | 22 | public class SerialAlgorithm 23 | { 24 | /**自定义进制(0,1没有加入,容易与o,l混淆)*/ 25 | private readonly char[] r ; 26 | /**自动补全组(不能与自定义进制有重复)*/ 27 | private readonly char[] b ; 28 | /**进制长度*/ 29 | private readonly int l; 30 | 31 | private readonly int bl ; 32 | /**序列最小长度*/ 33 | private readonly int s = 6; 34 | 35 | private readonly Random random = new Random(DateTime.Now.Millisecond); 36 | 37 | public SerialAlgorithm(char[] r, char[] b,int serialLength) 38 | { 39 | this.r = r; 40 | this.b = b; 41 | this.s = serialLength; 42 | 43 | l = r.Length; 44 | bl = b.Length; 45 | } 46 | 47 | /** 48 | * 根据ID生成六位随机码 49 | * @param num ID 50 | * @return 随机码 51 | */ 52 | public String toSerialNumber(long num) 53 | { 54 | char[] buf = new char[32]; 55 | int charPos = 32; 56 | 57 | while ((num / l) > 0) 58 | { 59 | buf[--charPos] = r[(int)(num % l)]; 60 | num /= l; 61 | } 62 | buf[--charPos] = r[(int)(num % l)]; 63 | String str = new String(buf, charPos, (32 - charPos)); 64 | //不够长度的自动随机补全 65 | if (str.Length < s) 66 | { 67 | StringBuilder sb = new StringBuilder(); 68 | Random rnd = new Random(); 69 | for (int i = 0; i < s - str.Length; i++) 70 | { 71 | sb.Append(b[rnd.Next(bl)]); 72 | } 73 | str += sb.ToString(); 74 | } 75 | return str.ToLower(); 76 | } 77 | 78 | /// 79 | /// 生成一个非重复的随机序列。 80 | /// 81 | /// 序列最小值。 82 | /// 序列最大值。 83 | /// 序列。 84 | private int[] BuildRandomSequence4(int low, int high) 85 | { 86 | int x = 0, tmp = 0; 87 | if (low > high) 88 | { 89 | tmp = low; 90 | low = high; 91 | high = tmp; 92 | } 93 | int[] array = new int[high - low + 1]; 94 | for (int i = low; i <= high; i++) 95 | { 96 | array[i - low] = i; 97 | } 98 | for (int i = array.Length - 1; i > 0; i--) 99 | { 100 | x = random.Next(0, i + 1); 101 | tmp = array[i]; 102 | array[i] = array[x]; 103 | array[x] = tmp; 104 | } 105 | return array; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Kadder/Grpc/Helper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using Kadder.Messaging; 5 | using Kadder.Streaming; 6 | 7 | namespace Kadder.Grpc 8 | { 9 | public static class Helper 10 | { 11 | public static CallType AnalyseCallType(Type parameterType, Type returnParameterType) 12 | { 13 | if(parameterType.IsGenericType) 14 | parameterType=parameterType.GetGenericTypeDefinition(); 15 | if(returnParameterType.IsGenericType) 16 | returnParameterType=returnParameterType.GetGenericTypeDefinition(); 17 | 18 | if (parameterType == typeof(IAsyncRequestStream<>) && returnParameterType == typeof(IAsyncResponseStream<>)) 19 | return CallType.DuplexStreamRpc; 20 | else if (parameterType == typeof(IAsyncRequestStream<>)) 21 | return CallType.ClientStreamRpc; 22 | else if (returnParameterType == typeof(IAsyncResponseStream<>)) 23 | return CallType.ServerStreamRpc; 24 | else 25 | return CallType.Rpc; 26 | } 27 | 28 | public static Type ParseMethodParameter(this MethodInfo method) 29 | { 30 | var methodName = method.Name; 31 | var servicerName = method.DeclaringType.FullName; 32 | 33 | var methodParameters = method.GetParameters(); 34 | if (methodParameters.Length == 0) 35 | return typeof(EmptyMessage); 36 | 37 | var parameterType = methodParameters[0].ParameterType; 38 | if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() != typeof(IAsyncRequestStream<>) && parameterType.IsByRef) 39 | throw new InvalidCastException($"The method({methodName}) ParameterType invalid! Servicer({servicerName})"); 40 | 41 | return parameterType; 42 | } 43 | 44 | public static Type ParseMethodReturnParameter(this MethodInfo method) 45 | { 46 | var methodName = method.Name; 47 | var servicerName = method.DeclaringType.FullName; 48 | var isVoidType = method.ReturnType == typeof(void) || (method.ReturnType == typeof(Task)); 49 | var parameters = method.GetParameters(); 50 | 51 | if (isVoidType && parameters.Length < 2) 52 | return typeof(EmptyMessageResult); 53 | 54 | var responseParameter = method.ReturnType; 55 | if (parameters.Length > 1) 56 | responseParameter = parameters[1].ParameterType; 57 | 58 | if (isVoidType && responseParameter.GetGenericTypeDefinition() == typeof(IAsyncResponseStream<>)) 59 | return responseParameter; 60 | if (!isVoidType && responseParameter.GetGenericTypeDefinition() == typeof(Task<>)) 61 | { 62 | return responseParameter.GenericTypeArguments[0]; 63 | } 64 | 65 | throw new InvalidCastException($"The method({methodName}) ReturnType is Invalid! Servicer({servicerName})"); 66 | } 67 | 68 | public static string GenerateAwaitResultCode(Type returnType) 69 | { 70 | if (returnType == typeof(EmptyMessageResult)) 71 | return string.Empty; 72 | return "var result = "; 73 | } 74 | 75 | public static string GenerateRequestCode(Type parameterType) 76 | { 77 | if (parameterType == typeof(EmptyMessage)) 78 | return string.Empty; 79 | return "request"; 80 | } 81 | 82 | public static string GenerateReturnCode(Type returnType,bool isClient=false) 83 | { 84 | if (returnType == typeof(EmptyMessageResult)&&!isClient) 85 | return "return new EmptyMessageResult();"; 86 | if (returnType == typeof(EmptyMessageResult)&&isClient) 87 | return string.Empty; 88 | return "return result;"; 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Kadder.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kadder", "Kadder\Kadder.csproj", "{DCA12F43-7F2D-49F0-92A8-DB2420D1147B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kadder.Test", "Kadder.Test\Kadder.Test.csproj", "{4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kadder.Maui", "Kadder.Maui\Kadder.Maui.csproj", "{D2D031D5-C1C1-4061-9725-DA3712092BD4}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionProperties) = preSolution 14 | HideSolutionNode = FALSE 15 | EndGlobalSection 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Debug|x64 = Debug|x64 19 | Debug|x86 = Debug|x86 20 | Release|Any CPU = Release|Any CPU 21 | Release|x64 = Release|x64 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|x64.Build.0 = Debug|Any CPU 29 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Debug|x86.Build.0 = Debug|Any CPU 31 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|x64.ActiveCfg = Release|Any CPU 34 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|x64.Build.0 = Release|Any CPU 35 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|x86.ActiveCfg = Release|Any CPU 36 | {DCA12F43-7F2D-49F0-92A8-DB2420D1147B}.Release|x86.Build.0 = Release|Any CPU 37 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|x64.Build.0 = Debug|Any CPU 41 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Debug|x86.Build.0 = Debug|Any CPU 43 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|x64.ActiveCfg = Release|Any CPU 46 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|x64.Build.0 = Release|Any CPU 47 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|x86.ActiveCfg = Release|Any CPU 48 | {4DFC1FE6-4EA1-4BC8-9D50-9974F91BC48A}.Release|x86.Build.0 = Release|Any CPU 49 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|x64.ActiveCfg = Debug|Any CPU 52 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|x64.Build.0 = Debug|Any CPU 53 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|x86.ActiveCfg = Debug|Any CPU 54 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Debug|x86.Build.0 = Debug|Any CPU 55 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|x64.ActiveCfg = Release|Any CPU 58 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|x64.Build.0 = Release|Any CPU 59 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|x86.ActiveCfg = Release|Any CPU 60 | {D2D031D5-C1C1-4061-9725-DA3712092BD4}.Release|x86.Build.0 = Release|Any CPU 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/HostExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using GenAssembly; 5 | using Grpc.Core; 6 | using Grpc.Core.Interceptors; 7 | using Kadder; 8 | using Kadder.Grpc.Server; 9 | using Kadder.Utils; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Logging; 13 | 14 | namespace Microsoft.Extensions.Hosting; 15 | 16 | public static class KadderGrpcServerHostExtension 17 | { 18 | public static IHostBuilder AddGrpcServer(this IHostBuilder hostBuilder, 19 | Action builderAction = null, 20 | string configurationKeyName = "GrpcServer") 21 | { 22 | hostBuilder.ConfigureServices((context, services) => 23 | { 24 | var log = services.BuildServiceProvider().GetService>(); 25 | 26 | var builder = context.Configuration.GetSection(configurationKeyName).Get() ?? 27 | new GrpcServerBuilder(); 28 | builderAction?.Invoke(context, services, builder); 29 | 30 | var codeBuilder = new CodeBuilder("Kadder.Grpc.Server"); 31 | if (!string.IsNullOrWhiteSpace(builder.CodeCacheDir)) 32 | CodeBuilder.CodeCachePath = builder.CodeCacheDir; 33 | if (!string.IsNullOrWhiteSpace(builder.DllCacheDir)) 34 | CodeBuilder.DllCachePath = builder.DllCacheDir; 35 | 36 | var server = new Server(builder.Options.ChannelOptions); 37 | foreach (var port in builder.Options.Ports) 38 | server.Ports.Add(new ServerPort(port.Host, port.Port, port.Credentials)); 39 | foreach (var interceptor in builder.Interceptors) 40 | services.AddSingleton(interceptor); 41 | 42 | foreach (var assemblyName in builder.AssemblyNames) 43 | builder.Assemblies.Add(Assembly.Load(assemblyName)); 44 | builder.Assemblies.Add(Assembly.GetEntryAssembly()); 45 | 46 | if (log.IsEnabled(LogLevel.Debug)) 47 | log.LogDebug(builder.ToString()); 48 | 49 | var servicerTypes = ServicerHelper.GetServicerTypes(builder.Assemblies); 50 | if (servicerTypes == null || servicerTypes.Count == 0) 51 | throw new ArgumentNullException("Not found any grpc servicer!"); 52 | 53 | var servicerProxyers = new ServicerProxyGenerator(builder.Options.PackageName, servicerTypes).Generate(); 54 | 55 | codeBuilder.CreateClass(servicerProxyers.ToArray()); 56 | codeBuilder.AddAssemblyRefence(Assembly.GetExecutingAssembly()) 57 | .AddAssemblyRefence(typeof(ILogger).Assembly) 58 | .AddAssemblyRefence(typeof(ServerServiceDefinition).Assembly) 59 | .AddAssemblyRefence(typeof(ServiceProviderServiceExtensions).Assembly) 60 | .AddAssemblyRefence(typeof(Console).Assembly) 61 | .AddAssemblyRefence(servicerTypes.Select(p => p.Assembly).Distinct().ToArray()) 62 | .AddAssemblyRefence(typeof(KadderBuilder).Assembly) 63 | .AddAssemblyRefence(typeof(GrpcServerOptions).Assembly) 64 | .AddAssemblyRefence(builder.GetType().Assembly); 65 | 66 | var codeAssembly = codeBuilder.BuildAsync().Result; 67 | foreach (var servicerProxyer in servicerProxyers) 68 | { 69 | var namespaces = $"{servicerProxyer.Namespace}.{servicerProxyer.Name}"; 70 | var proxyerType = codeAssembly.Assembly.GetType(namespaces); 71 | services.AddSingleton(proxyerType); 72 | builder.GrpcServicerProxyers.Add(proxyerType); 73 | } 74 | 75 | services.AddSingleton(server); 76 | services.AddSingleton(builder); 77 | services.AddSingleton(); 78 | services.AddSingleton(typeof(KadderBuilder), builder); 79 | services.AddSingleton(); 80 | }); 81 | return hostBuilder; 82 | } 83 | 84 | public static IHost StartGrpcServer(this IHost host) 85 | { 86 | var provider = host.Services; 87 | var builder = provider.GetService(); 88 | var server = provider.GetService(); 89 | 90 | var intercetors = new Interceptor[builder.Interceptors.Count]; 91 | for (var i = 0; i < builder.Interceptors.Count; i++) 92 | intercetors[i] = (Interceptor) provider.GetService(builder.Interceptors[i]); 93 | 94 | foreach (var serviceProxyer in builder.GrpcServicerProxyers) 95 | { 96 | var definition = ((IGrpcServices) provider.GetService(serviceProxyer)).BindServices(); 97 | definition = definition.Intercept(intercetors); 98 | server.Services.Add(definition); 99 | } 100 | 101 | server.Start(); 102 | return host; 103 | } 104 | } -------------------------------------------------------------------------------- /Kadder/Grpc/Client/ServicerInvoker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Threading.Tasks; 5 | using Grpc.Core; 6 | using Kadder.Streaming; 7 | using Kadder.Utils; 8 | 9 | namespace Kadder.Grpc.Client 10 | { 11 | public class ServicerInvoker 12 | { 13 | private readonly IBinarySerializer _serializer; 14 | private readonly ConcurrentDictionary _methods; 15 | private readonly IObjectProvider _provider; 16 | 17 | public ServicerInvoker(IBinarySerializer serializer, IObjectProvider provider) 18 | { 19 | _serializer = serializer; 20 | _provider = provider; 21 | _methods = new ConcurrentDictionary(); 22 | } 23 | 24 | public async Task RpcAsync(TRequest request, string service, string methodName) where TRequest : class where TResponse : class 25 | { 26 | var proxyer = getProxyer(service); 27 | var channelInfo = proxyer.GetChannel(); 28 | var invoker = channelInfo.GetInvoker(_provider); 29 | var method = GetMethod(service, methodName, MethodType.Unary); 30 | 31 | var result = invoker.AsyncUnaryCall(method, channelInfo.Options.Address, new CallOptions(), request); 32 | return await result.ResponseAsync; 33 | } 34 | 35 | public Task ClientStreamAsync(IAsyncRequestStream request, string service, string methodName) where TRequest : class where TResponse : class 36 | { 37 | var client = getProxyer(service); 38 | var channelInfo = client.GetChannel(); 39 | var invoker = channelInfo.GetInvoker(_provider); 40 | var method = GetMethod(service, methodName, MethodType.ClientStreaming); 41 | 42 | var result = invoker.AsyncClientStreamingCall(method, channelInfo.Options.Address, new CallOptions()); 43 | var requestStream = (AsyncRequestStream)request; 44 | requestStream.StreamWriter = result.RequestStream; 45 | return result.ResponseAsync; 46 | } 47 | 48 | public Task ServerStreamAsync(TRequest request, IAsyncResponseStream response, string service, string methodName) where TRequest : class where TResponse : class 49 | { 50 | var client = getProxyer(service); 51 | var channelInfo = client.GetChannel(); 52 | var invoker = channelInfo.GetInvoker(_provider); 53 | var method = GetMethod(service, methodName, MethodType.ClientStreaming); 54 | 55 | var result = invoker.AsyncServerStreamingCall(method, channelInfo.Options.Address, new CallOptions(), request); 56 | var responseStream = (AsyncResponseStream)response; 57 | responseStream.StreamReader = result.ResponseStream; 58 | return result.ResponseHeadersAsync; 59 | } 60 | 61 | public Task DuplexStreamAsync(IAsyncRequestStream request, IAsyncResponseStream response, string service, string methodName) where TRequest : class where TResponse : class 62 | { 63 | var client = getProxyer(service); 64 | var channelInfo = client.GetChannel(); 65 | var invoker = channelInfo.GetInvoker(_provider); 66 | var method = GetMethod(service, methodName, MethodType.ClientStreaming); 67 | 68 | var result = invoker.AsyncDuplexStreamingCall(method, channelInfo.Options.Address, new CallOptions()); 69 | var requestStream = (AsyncRequestStream)request; 70 | var responseStream = (AsyncResponseStream)response; 71 | requestStream.StreamWriter = result.RequestStream; 72 | responseStream.StreamReader = result.ResponseStream; 73 | return result.ResponseHeadersAsync; 74 | } 75 | 76 | private GrpcProxyer getProxyer(string service) 77 | { 78 | if (!GrpcProxyer.ProxyerDict.TryGetValue(service, out GrpcProxyer proxyer)) 79 | throw new KeyNotFoundException($"Cannot found proxyer! Servicer({service})"); 80 | return proxyer; 81 | } 82 | 83 | private Method GetMethod(string service, string methodName, MethodType methodType) 84 | { 85 | var key = $"{service}{methodName}"; 86 | if (_methods.TryGetValue(key, out IMethod method)) 87 | return (Method)method; 88 | 89 | var requestMarshaller = new Marshaller(_serializer.Serialize, _serializer.Deserialize); 90 | var responseMarshaller = new Marshaller(_serializer.Serialize, _serializer.Deserialize); 91 | var newMethod = new Method(methodType, service, methodName, requestMarshaller, responseMarshaller); 92 | _methods.TryAdd(key, newMethod); 93 | return newMethod; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Kadder/GrpcConfiguration.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Threading.Tasks; 3 | // using GenAssembly; 4 | // using Kadder.Middlewares; 5 | // using Kadder.Utilies; 6 | // using Microsoft.Extensions.DependencyInjection; 7 | 8 | namespace Kadder 9 | { 10 | public static class GrpcConfiguration 11 | { 12 | // public static IServiceCollection AddKadderGrpcClient( 13 | // this IServiceCollection services, Action builderAction) 14 | // { 15 | // var builder = new GrpcClientBuilder(); 16 | // builderAction(builder); 17 | 18 | // var serviceCallBuilder = new GrpcServiceCallBuilder(); 19 | // services.AddSingleton(builder); 20 | // services.AddSingleton(serviceCallBuilder); 21 | // services.RegSerializer(null, builder.BinarySerializer); 22 | 23 | // foreach(var interceptor in builder.Interceptors) 24 | // { 25 | // services.AddSingleton(interceptor); 26 | // } 27 | 28 | // foreach (var clientMetadata in builder.ClientMetadatas) 29 | // { 30 | // var grpcClient = new GrpcClient(clientMetadata,builder, serviceCallBuilder); 31 | // foreach(var interceptor in clientMetadata.PrivateInterceptors) 32 | // { 33 | // services.AddSingleton(interceptor); 34 | // } 35 | // foreach (var item in grpcClient.GrpcServiceDic) 36 | // { 37 | // services.AddSingleton(item.Key, item.Value); 38 | // } 39 | // } 40 | 41 | // return services; 42 | // } 43 | 44 | // public static IServiceProvider ApplyKadderGrpcClient(this IServiceProvider provider) 45 | // { 46 | // GrpcClientBuilder.ServiceProvider = provider; 47 | // return provider; 48 | // } 49 | 50 | // public static IServiceCollection AddKadderGrpcServer( 51 | // this IServiceCollection services, Action builderAction) 52 | // { 53 | // var builder = new GrpcServerBuilder(); 54 | // builderAction(builder); 55 | // var serviceBuilder = new GrpcServiceBuilder(); 56 | // services.AddSingleton(builder); 57 | // services.RegSerializer(builder.JsonSerializer, builder.BinarySerializer); 58 | // services.AddSingleton(); 59 | // services.AddSingleton(serviceBuilder); 60 | // services.AddSingleton(); 61 | // foreach (var item in builder.Middlewares) 62 | // { 63 | // Middlewares.GrpcHandlerDirector.AddMiddleware(item); 64 | // } 65 | // foreach (var interceptor in builder.Interceptors) 66 | // { 67 | // services.AddSingleton(interceptor); 68 | // } 69 | 70 | // var namespaces = "Kadder.CodeGeneration"; 71 | // var codeBuilder = new CodeBuilder(namespaces, namespaces); 72 | // var grpcClasses = serviceBuilder.GenerateGrpcProxy(builder.Options, codeBuilder); 73 | // // var proxyCode = serviceBuilder.GenerateHandlerProxy(builder.Options.GetScanAssemblies(), codeBuilder); 74 | // var codeAssembly = codeBuilder.BuildAsync().Result; 75 | 76 | // foreach(var grpcClass in grpcClasses) 77 | // { 78 | // namespaces = $"{grpcClass.Namespace}.{grpcClass.Name}"; 79 | // var grpcType = codeAssembly.Assembly.GetType(namespaces); 80 | // services.AddSingleton(grpcType); 81 | // builder.Services.Add(grpcType); 82 | // } 83 | 84 | // services.AddSingleton(); 85 | 86 | // return services; 87 | // } 88 | 89 | // public static IServiceProvider StartKadderGrpc(this IServiceProvider provider) 90 | // { 91 | // GrpcServerBuilder.ServiceProvider = provider; 92 | // var server = provider.GetService(); 93 | // server.Start(); 94 | // return provider; 95 | // } 96 | 97 | // public static IServiceProvider ShutdownKadderGrpc(this IServiceProvider provider, Func action = null) 98 | // { 99 | // var server = provider.GetService(); 100 | // server.ShutdownAsync(action).Wait(); 101 | // return provider; 102 | // } 103 | 104 | // private static void RegSerializer(this IServiceCollection services, IJsonSerializer jsonSerializer, 105 | // IBinarySerializer binarySerializer) 106 | // { 107 | // if (jsonSerializer == null) 108 | // { 109 | // services.AddSingleton(); 110 | // } 111 | // else 112 | // { 113 | // services.AddSingleton(jsonSerializer); 114 | // } 115 | // if (binarySerializer == null) 116 | // { 117 | // services.AddSingleton(); 118 | // } 119 | // else 120 | // { 121 | // services.AddSingleton(binarySerializer); 122 | // } 123 | 124 | // } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Kadder/Utils/Ensure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace Kadder.Utilities 9 | { 10 | public static class Ensure 11 | { 12 | 13 | public static void NotNull(T obj,string errorMsg,EnsureScope scope=null,Exception innerException=null) 14 | { 15 | if (obj == null) throw new EnsureException(errorMsg,scope, innerException); 16 | } 17 | 18 | public static void NotNull(IList list,string errorMsg,EnsureScope scope=null, Exception innerException=null) 19 | { 20 | if (list == null||list.Count()==0) throw new EnsureException(errorMsg,scope, innerException); 21 | } 22 | 23 | public static void NotNullOrWhiteSpace(string str,string errorMsg,EnsureScope scope=null,Exception innerException=null) 24 | { 25 | if (string.IsNullOrWhiteSpace(str)) throw new EnsureException(errorMsg, scope,innerException); 26 | } 27 | 28 | public static void GrandThan(double maxValue,double minValue,string errorMsg,bool hasAllowEqual=true,EnsureScope scope=null) 29 | { 30 | if (hasAllowEqual && maxValue < minValue) throw new EnsureException(errorMsg, scope); 31 | if (!hasAllowEqual && maxValue <= minValue) throw new EnsureException(errorMsg, scope); 32 | } 33 | 34 | public static void GrandThan(long maxValue,long minValue,string errorMsg,bool hasAllowEqual=true,EnsureScope scope=null) 35 | { 36 | if (hasAllowEqual && maxValue < minValue) throw new EnsureException(errorMsg, scope); 37 | if (!hasAllowEqual && maxValue <= minValue) throw new EnsureException(errorMsg, scope); 38 | } 39 | 40 | public static void GrandThan(decimal maxValue,decimal minValue,string errorMsg,bool hasAllowEqual=true,EnsureScope scope=null) 41 | { 42 | if (hasAllowEqual && maxValue < minValue) throw new EnsureException(errorMsg, scope); 43 | if (!hasAllowEqual && maxValue <= minValue) throw new EnsureException(errorMsg, scope); 44 | } 45 | 46 | public static void GrandThan(DateTime maxValue,DateTime minValue,string errorMsg,bool hasAllowEqual=true,EnsureScope scope=null) 47 | { 48 | if (hasAllowEqual && maxValue < minValue) throw new EnsureException(errorMsg, scope); 49 | if (!hasAllowEqual && maxValue <= minValue) throw new EnsureException(errorMsg, scope); 50 | } 51 | 52 | public static void MustBeNull(IList list,string errorMsg,EnsureScope scope=null) 53 | { 54 | if (list != null && list.Count != 0) throw new EnsureException(errorMsg, scope); 55 | } 56 | 57 | public static void MustBeNull(T obj,string errorMsg,EnsureScope scope=null) 58 | { 59 | if (obj != null ) throw new EnsureException(errorMsg, scope); 60 | } 61 | 62 | public static void MustBeEqual(int left,int right,string errorMsg,EnsureScope scope=null) 63 | { 64 | if (left != right) throw new EnsureException(errorMsg, scope); 65 | } 66 | 67 | public static void MustBeNoEqual(int left, int right, string errorMsg,EnsureScope scope=null) 68 | { 69 | if (left == right) throw new EnsureException(errorMsg, scope); 70 | } 71 | 72 | public static void MustBeTrue(bool value,string errorMsg,EnsureScope scope=null) 73 | { 74 | if (!value) throw new EnsureException(errorMsg, scope); 75 | } 76 | 77 | public static void MustBeFalse(bool value, string errorMsg,EnsureScope scope=null) 78 | { 79 | if (value) throw new EnsureException(errorMsg, scope); 80 | } 81 | 82 | public static void MustBeNum(string numStr,string errorMsg,EnsureScope scope=null) 83 | { 84 | if (!Regex.IsMatch(numStr, @"^[0-9]*$")) throw new EnsureException(errorMsg, scope); 85 | } 86 | 87 | } 88 | 89 | public class EnsureScope 90 | { 91 | public static EnsureScope Default=new EnsureScope(){ScopeName="System"}; 92 | 93 | public string ScopeName{get;set;} 94 | } 95 | 96 | public class EnsureException:System.Exception 97 | { 98 | public EnsureException(string message,EnsureScope scope=null,Exception innerException=null):base(message,innerException) 99 | { 100 | Scope=scope??EnsureScope.Default; 101 | } 102 | 103 | public EnsureScope Scope{get;set;} 104 | 105 | /// 106 | /// if eat success, we can return ensureexception. 107 | /// if eat failed, we will be return origin exception. 108 | /// 109 | /// exception or ensureexception 110 | /// 111 | public static bool EatException(ref Exception ex) 112 | { 113 | var ensureException = ex; 114 | while (true) 115 | { 116 | if (ensureException == null) break; 117 | if (ensureException is EnsureException) break; 118 | ensureException = ensureException.InnerException; 119 | } 120 | if (ensureException is EnsureException) 121 | { 122 | ex=ensureException; 123 | return true; 124 | } 125 | else 126 | { 127 | return false; 128 | } 129 | } 130 | 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Kadder/Utils/Exetension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Kadder.Utilies 6 | { 7 | public static class Exetension 8 | { 9 | public static bool EatException(this Exception ex, out EatException outEx) 10 | where EatException : Exception 11 | { 12 | var ensureException = ex; 13 | while (true) 14 | { 15 | if (ensureException == null) 16 | { 17 | break; 18 | } 19 | if (ensureException is EatException) 20 | { 21 | break; 22 | } 23 | ensureException = ensureException.InnerException; 24 | } 25 | if (ensureException is EatException) 26 | { 27 | outEx = (EatException)ensureException; 28 | return true; 29 | } 30 | else 31 | { 32 | outEx = null; 33 | return false; 34 | } 35 | } 36 | 37 | /// 38 | /// Timestamp to datetime 39 | /// 40 | /// source timestamp 41 | /// target datetime type 42 | /// 43 | public static DateTime ToTime(this long timestamp, TimeType type = TimeType.Beijing) 44 | { 45 | DateTime dtStart; 46 | switch (type) 47 | { 48 | case TimeType.Beijing: dtStart = DateTime.Parse("1970/01/01 08:00:00"); break; 49 | default: dtStart = DateTime.Parse("1970/01/01 00:00:00"); break; 50 | } 51 | return dtStart.AddSeconds(timestamp); 52 | } 53 | 54 | public static string CheckTime(this string dateTimeStr, DateTime? defaultTime = null, string format = "yyyy/MM/dd HH:mm:ss") 55 | { 56 | DateTime dateTime; 57 | if (!DateTime.TryParse(dateTimeStr, out dateTime)) 58 | { 59 | dateTime = defaultTime.HasValue ? defaultTime.Value : DateTime.MinValue; 60 | } 61 | return dateTime.ToString(format); 62 | } 63 | 64 | public static DateTime MaxTime() 65 | { 66 | return DateTime.Parse("2999/01/01 00:00:00"); 67 | } 68 | 69 | public static string ToSqlInWithInt(this IList values) 70 | { 71 | if (values == null || values.Count == 0) return string.Empty; 72 | string strValues = string.Empty; 73 | foreach (var item in values) strValues += $"{item},"; 74 | return strValues.Remove(strValues.Length - 1); 75 | } 76 | 77 | public static DateTime GetWeekTargetDay(this DateTime time, DayOfWeek targetDayOfWeek) 78 | { 79 | int targetDayOfWeekInt = targetDayOfWeek == DayOfWeek.Sunday ? 7 : (int)targetDayOfWeek; 80 | int currentDayOfWeekInt = time.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)time.DayOfWeek; 81 | return time.AddDays(-(currentDayOfWeekInt - targetDayOfWeekInt)); 82 | } 83 | 84 | public static int ToInt(this string str, int defaultValue = 0) 85 | { 86 | int value = defaultValue; 87 | if (!int.TryParse(str, out value)) return defaultValue; 88 | return value; 89 | } 90 | 91 | /// 92 | /// DateTime convert tp timestamp 93 | /// 94 | /// source time (like this: yyyy/MM/dd HH:mm:ss) 95 | /// source time type 96 | /// 97 | public static long ToTimestamp(this string dateTimeStr, TimeType type = TimeType.Beijing) 98 | { 99 | DateTime dateTime; 100 | if (!DateTime.TryParse(dateTimeStr, out dateTime)) return 0; 101 | DateTime dtStart; 102 | switch (type) 103 | { 104 | case TimeType.Beijing: dtStart = DateTime.Parse("1970/01/01 08:00:00"); break; 105 | default: dtStart = DateTime.Parse("1970/01/01 00:00:00"); break; 106 | } 107 | return (long)(dateTime - dtStart).TotalSeconds; 108 | } 109 | 110 | public static string GetExceptionMessage(this Exception exception) 111 | { 112 | StringBuilder error = new StringBuilder(exception.Message); 113 | var trim = " "; 114 | while (true) 115 | { 116 | if (exception.InnerException != null) 117 | { 118 | error.AppendLine($"{trim}InnerException"); 119 | error.AppendLine($"{trim} Message --> {exception.InnerException.Message}"); 120 | error.AppendLine($"{trim} StackTrace --> {exception.InnerException.StackTrace}"); 121 | 122 | exception = exception.InnerException; 123 | } 124 | else 125 | { 126 | return error.ToString(); 127 | } 128 | } 129 | } 130 | 131 | public static string GetCloseTrace(this Exception exception) 132 | { 133 | var traces = exception.StackTrace.Split('\n'); 134 | if (traces == null || traces.Length == 0) return exception.StackTrace; 135 | return traces[0]; 136 | } 137 | 138 | public static IList ToList(this string str, char split) 139 | { 140 | if (string.IsNullOrWhiteSpace(str)) return new List(); 141 | var ids = new List(); 142 | foreach (var item in str.Split(split)) 143 | { 144 | ids.Add(item.ToInt(0)); 145 | } 146 | return ids; 147 | } 148 | 149 | public static DateTime ToDateTime(this string dateTimeStr, DateTime? defaultTime = null) 150 | { 151 | DateTime dateTime; 152 | if (!DateTime.TryParse(dateTimeStr, out dateTime)) 153 | { 154 | dateTime = defaultTime.HasValue ? defaultTime.Value : DateTime.MinValue; 155 | } 156 | return dateTime; 157 | } 158 | 159 | public static string ToValOrDefault(this string value, string defaultValue) 160 | { 161 | if (string.IsNullOrWhiteSpace(value)) return defaultValue; 162 | else return value; 163 | } 164 | 165 | public static string ToFrontString(this DateTime time) 166 | { 167 | return time.ToString("yyyy/MM/dd HH:mm:ss"); 168 | } 169 | 170 | public static string ToStr(this IList values, string split = ",") 171 | { 172 | if (values == null || values.Count == 0) return string.Empty; 173 | 174 | var strValue = new StringBuilder(); 175 | foreach (var str in values) 176 | { 177 | strValue.Append(str); 178 | strValue.Append(","); 179 | } 180 | return strValue.ToString().Remove(strValue.Length - 1); 181 | } 182 | } 183 | 184 | public enum TimeType 185 | { 186 | Beijing = 1 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Kadder/Grpc/Client/ServicerProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using GenAssembly; 7 | using GenAssembly.Descripters; 8 | using Grpc.Core; 9 | using Kadder.Messaging; 10 | using Kadder.Utils; 11 | 12 | namespace Kadder.Grpc.Client 13 | { 14 | public class ServicerProxyGenerator 15 | { 16 | public const string ClassServicerInvokerName = "_invoker"; 17 | 18 | private readonly string _packageName; 19 | private readonly IList _servicerTypes; 20 | 21 | public ServicerProxyGenerator(string packageName, IList servicerTypes) 22 | { 23 | _packageName = packageName; 24 | _servicerTypes = servicerTypes; 25 | } 26 | 27 | public IList Generate() 28 | { 29 | var proxyerDescripters = new List(); 30 | foreach (var servicerType in _servicerTypes) 31 | proxyerDescripters.Add(generate(servicerType)); 32 | 33 | return proxyerDescripters; 34 | } 35 | 36 | private ClassDescripter generate(Type servicerType) 37 | { 38 | var classDescripter = generateClass(servicerType); 39 | generateField(ref classDescripter); 40 | generateConstructor(ref classDescripter); 41 | 42 | var grpcMethods = ServicerHelper.GetMethod(servicerType); 43 | foreach (var method in grpcMethods) 44 | classDescripter.CreateMember(generateMethod(ref classDescripter, method)); 45 | classDescripter.CreateMember(generateBindServicesMethod(ref classDescripter)); 46 | 47 | return classDescripter; 48 | } 49 | 50 | #region genclass 51 | private ClassDescripter generateClass(Type servicerType) 52 | { 53 | var servicerName = $"KadderClient{servicerType.Name}"; 54 | var namespaceName = getNamespaceName(servicerType); 55 | 56 | var classDescripter = new ClassDescripter(servicerName, namespaceName) 57 | .SetBaseType(servicerType.Name) 58 | .AddUsing(servicerType.Namespace) 59 | .AddUsing( 60 | "using Grpc.Core;", 61 | "using System.Threading.Tasks;", 62 | "using Kadder;", 63 | "using Kadder.Utilies;", 64 | "using Kadder.Utils;", 65 | "using Microsoft.Extensions.DependencyInjection;", 66 | "using Kadder.Messaging;"); 67 | classDescripter.SetAccess(AccessType.Public); 68 | return classDescripter; 69 | } 70 | 71 | private void generateField(ref ClassDescripter classDescripter) 72 | { 73 | var invokerField = new FieldDescripter(ClassServicerInvokerName) 74 | .SetType(typeof(ServicerInvoker)); 75 | invokerField.SetAccess(AccessType.PrivateReadonly); 76 | 77 | classDescripter.CreateFiled(invokerField) 78 | .AddUsing(typeof(ServicerInvoker).Namespace); 79 | } 80 | 81 | private void generateConstructor(ref ClassDescripter classDescripter) 82 | { 83 | var constructor = new ConstructorDescripter(classDescripter.Name); 84 | constructor.SetAccess(AccessType.Public); 85 | 86 | var providerParameter = new ParameterDescripter(typeof(ServicerInvoker).Name, "invoker"); 87 | constructor.SetParams(providerParameter); 88 | 89 | var code = $@" 90 | {ClassServicerInvokerName} = invoker;"; 91 | constructor.SetCode(code); 92 | 93 | classDescripter.CreateConstructor(constructor); 94 | } 95 | #endregion 96 | 97 | #region genmethod 98 | private MethodDescripter generateMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo) 99 | { 100 | if (methodInfo.CustomAttributes.FirstOrDefault(p => p.AttributeType == typeof(NotGrpcMethodAttribute)) != null) 101 | return generateNoGrpcMethod(ref classDescripter, methodInfo); 102 | 103 | var parameterType = methodInfo.ParseMethodParameter(); 104 | var returnType = methodInfo.ParseMethodReturnParameter(); 105 | var callType = Helper.AnalyseCallType(parameterType, returnType); 106 | 107 | classDescripter.AddUsing(parameterType.Namespace, returnType.Namespace, methodInfo.DeclaringType.Namespace); 108 | 109 | var method = new MethodDescripter("", classDescripter); 110 | switch (callType) 111 | { 112 | case CallType.Rpc: 113 | method = generateRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 114 | break; 115 | case CallType.ClientStreamRpc: 116 | method = generateClientStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 117 | break; 118 | case CallType.ServerStreamRpc: 119 | method = generateServerStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 120 | break; 121 | case CallType.DuplexStreamRpc: 122 | method = generateDuplexStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 123 | break; 124 | default: 125 | throw new InvalidOperationException("Invalid Method definition!"); 126 | } 127 | 128 | return method; 129 | } 130 | 131 | private MethodDescripter generateRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 132 | { 133 | var resultCode = Helper.GenerateAwaitResultCode(returnType); 134 | var resultType = generateRpcResponseType(returnType); 135 | var servicerName = getServicerName(methodInfo.DeclaringType); 136 | var methodName = methodInfo.Name.Replace("Async", ""); 137 | 138 | var method = generateMethodHead(ref classDescripter, methodInfo); 139 | if (parameterType != typeof(EmptyMessage)) 140 | method.Parameters.Add(new ParameterDescripter(parameterType.Name, "request")); 141 | else 142 | method.AppendCode("var request = new EmptyMessage();"); 143 | 144 | method.AppendCode($@"{resultCode} await {ClassServicerInvokerName}.RpcAsync<{parameterType.Name}, {returnType.Name}>(request, ""{servicerName}"", ""{methodName}""); 145 | {Helper.GenerateReturnCode(returnType, true)}"); 146 | method.SetReturnType(resultType); 147 | 148 | return method; 149 | } 150 | 151 | private MethodDescripter generateClientStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 152 | { 153 | var servicerName = getServicerName(methodInfo.DeclaringType); 154 | var resultCode = Helper.GenerateAwaitResultCode(returnType); 155 | var resultType = generateRpcResponseType(returnType); 156 | var methodName = methodInfo.Name.Replace("Async", ""); 157 | var requestType = parameterType.GenericTypeArguments[0]; 158 | 159 | var method = generateMethodHead(ref classDescripter, methodInfo, false); 160 | method.Parameters.Add(new ParameterDescripter($"IAsyncRequestStream<{requestType.Name}>", "request")); 161 | 162 | method.AppendCode($@"{resultCode}{ClassServicerInvokerName}.ClientStreamAsync<{requestType.Name}, {returnType.Name}>(request, ""{servicerName}"", ""{methodName}""); 163 | {Helper.GenerateReturnCode(returnType, true)}"); 164 | method.SetReturnType(resultType); 165 | 166 | return method; 167 | } 168 | 169 | private MethodDescripter generateServerStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 170 | { 171 | var servicerName = getServicerName(methodInfo.DeclaringType); 172 | var requestCode = Helper.GenerateRequestCode(parameterType); 173 | var methodName = methodInfo.Name.Replace("Async", ""); 174 | var responseType = returnType.GenericTypeArguments[0]; 175 | 176 | if (!string.IsNullOrWhiteSpace(requestCode)) 177 | requestCode += ", "; 178 | 179 | var method = generateMethodHead(ref classDescripter, methodInfo, false); 180 | method.Parameters.Add(new ParameterDescripter(parameterType.Name, "request")); 181 | method.Parameters.Add(new ParameterDescripter($"IAsyncResponseStream<{responseType.Name}>", "response")); 182 | 183 | method.AppendCode($@"return {ClassServicerInvokerName}.ServerStreamAsync<{parameterType.Name}, {responseType.Name}>({requestCode}response, ""{servicerName}"", ""{methodName}"");"); 184 | method.SetReturnType("Task"); 185 | 186 | return method; 187 | } 188 | 189 | private MethodDescripter generateDuplexStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 190 | { 191 | var servicerName = getServicerName(methodInfo.DeclaringType); 192 | var methodName = methodInfo.Name.Replace("Async", ""); 193 | var requestType = parameterType.GenericTypeArguments[0]; 194 | var responseType = returnType.GenericTypeArguments[0]; 195 | 196 | var method = generateMethodHead(ref classDescripter, methodInfo, false); 197 | method.Parameters.Add(new ParameterDescripter($"IAsyncRequestStream<{requestType.Name}>", "request")); 198 | method.Parameters.Add(new ParameterDescripter($"IAsyncResponseStream<{responseType.Name}>", "response")); 199 | 200 | method.AppendCode($@"return {ClassServicerInvokerName}.DuplexStreamAsync<{requestType.Name}, {responseType.Name}>(request, response, ""{servicerName}"", ""{methodName}"");"); 201 | method.SetReturnType("Task"); 202 | 203 | return method; 204 | } 205 | 206 | private MethodDescripter generateNoGrpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo) 207 | { 208 | var method = new MethodDescripter(methodInfo.Name, classDescripter, false); 209 | method.Access = AccessType.Public; 210 | method.SetReturnType(GetReturnName(ref classDescripter, methodInfo.ReturnType)); 211 | 212 | var parameterDescripters = new List(); 213 | foreach (var param in methodInfo.GetParameters()) 214 | { 215 | classDescripter.AddUsing(param.ParameterType.Namespace); 216 | method.Parameters.Add(new ParameterDescripter(GetReturnName(ref classDescripter, param.ParameterType), param.Name)); 217 | } 218 | 219 | method.AppendCode("throw new System.NotImplementedException();"); 220 | 221 | return method; 222 | } 223 | 224 | private MethodDescripter generateMethodHead(ref ClassDescripter classDescripter, MethodInfo methodInfo, bool isAsync = true) 225 | { 226 | var method = new MethodDescripter(methodInfo.Name, classDescripter, isAsync); 227 | method.Access = AccessType.Public; 228 | return method; 229 | } 230 | 231 | private string generateRpcResponseType(Type returnType) 232 | { 233 | if (returnType.Name == typeof(EmptyMessageResult).Name) 234 | return "Task"; 235 | 236 | return $"Task<{returnType.Name}>"; 237 | } 238 | 239 | private string GetReturnName(ref ClassDescripter classDescripter, Type type) 240 | { 241 | classDescripter.AddUsing(type.Namespace); 242 | 243 | if (type.IsGenericType) 244 | { 245 | var typeName = $"{type.FullName.Split('`')[0]}<"; 246 | foreach (var itemType in type.GenericTypeArguments) 247 | { 248 | typeName += $"{GetReturnName(ref classDescripter, itemType)},"; 249 | } 250 | return $"{typeName.Remove(typeName.Length - 1)}>"; 251 | } 252 | else if (type.IsValueType || type.Name.StartsWith("String")) 253 | { 254 | switch (type.Name) 255 | { 256 | case "Int16": return "short"; 257 | case "Int32": return "int"; 258 | case "Int64": return "long"; 259 | case "UInt16": return "ushort"; 260 | case "UInt32": return "uint"; 261 | case "UInt64": return "ulong"; 262 | case "String": return "string"; 263 | case "Double": return "double"; 264 | case "Single": return "float"; 265 | case "Decimal": return "decimal"; 266 | case "Boolean": return "bool"; 267 | default: return string.Empty; 268 | } 269 | } 270 | else 271 | { 272 | return type.FullName; 273 | } 274 | } 275 | #endregion 276 | 277 | #region GrpcCallMethod 278 | private MethodDescripter generateBindServicesMethod(ref ClassDescripter classDescripter) 279 | { 280 | var bindServicesMethod = new MethodDescripter("BindServices", classDescripter); 281 | bindServicesMethod.Access = AccessType.Public; 282 | bindServicesMethod.SetReturnType(typeof(ServerServiceDefinition)); 283 | bindServicesMethod.AppendCode(@"throw new NotImplementedException();"); 284 | return bindServicesMethod; 285 | } 286 | #endregion 287 | 288 | private string getNamespaceName(Type servicerType) 289 | { 290 | var namespaceName = servicerType.Namespace; 291 | if (!string.IsNullOrWhiteSpace(_packageName)) 292 | namespaceName = _packageName; 293 | return namespaceName; 294 | } 295 | 296 | private string getServicerName(Type servicerType) 297 | { 298 | var servicerName = servicerType.FullName; 299 | if (!string.IsNullOrWhiteSpace(_packageName)) 300 | servicerName = $"{_packageName}.{servicerType.Name}"; 301 | return servicerName; 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /Kadder/Grpc/Server/ServicerProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using GenAssembly; 7 | using GenAssembly.Descripters; 8 | using Grpc.Core; 9 | using Kadder.Utils; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace Kadder.Grpc.Server 13 | { 14 | public class ServicerProxyGenerator 15 | { 16 | public const string ClassProviderName = "_provider"; 17 | public const string ClassBinarySerializerName = "_binarySerializer"; 18 | public const string ClassLoggerName = "_log"; 19 | public const string FakeCallTypeAttributeName = "FakeCallType"; 20 | 21 | private readonly List _servicerTypes; 22 | private readonly string _packageName; 23 | 24 | public ServicerProxyGenerator(string packageName, List servicerTypes) 25 | { 26 | _packageName = packageName; 27 | _servicerTypes = servicerTypes; 28 | } 29 | 30 | public List Generate() 31 | { 32 | var classDescripterList = new List(); 33 | 34 | foreach (var servicerType in _servicerTypes) 35 | classDescripterList.Add(generate(servicerType)); 36 | 37 | return classDescripterList; 38 | } 39 | 40 | private ClassDescripter generate(Type servicerType) 41 | { 42 | var classDescripter = generateClass(servicerType); 43 | generateField(ref classDescripter); 44 | generateConstructor(ref classDescripter); 45 | 46 | var grpcMethods = servicerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); 47 | foreach (var method in grpcMethods) 48 | { 49 | if (method.CustomAttributes.FirstOrDefault(p => p.AttributeType == typeof(NotGrpcMethodAttribute)) != null) 50 | continue; 51 | 52 | classDescripter.CreateMember(generateMethod(ref classDescripter, method)); 53 | } 54 | classDescripter.CreateMember(generateBindServicesMethod(ref classDescripter, servicerType)); 55 | 56 | return classDescripter; 57 | } 58 | 59 | #region class 60 | private ClassDescripter generateClass(Type servicerType) 61 | { 62 | var servicerName = $"KadderServer{servicerType.Name}"; 63 | var namespaceName = servicerType.Namespace; 64 | if (!string.IsNullOrWhiteSpace(_packageName)) 65 | namespaceName = _packageName; 66 | 67 | var classDescripter = new ClassDescripter(servicerName, namespaceName) 68 | .SetBaseType(typeof(IGrpcServices).Name) 69 | .AddUsing(typeof(IGrpcServices).Namespace) 70 | .AddUsing( 71 | "using Grpc.Core;", 72 | "using System.Threading.Tasks;", 73 | "using Kadder;", 74 | "using Kadder.Utilies;", 75 | "using Kadder.Utils;", 76 | "using Microsoft.Extensions.DependencyInjection;", 77 | "using Kadder.Messaging;"); 78 | classDescripter.SetAccess(AccessType.Public); 79 | return classDescripter; 80 | } 81 | 82 | private void generateField(ref ClassDescripter classDescripter) 83 | { 84 | var binarySerializerField = new FieldDescripter(ClassBinarySerializerName) 85 | .SetType(typeof(IBinarySerializer)); 86 | binarySerializerField.SetAccess(AccessType.PrivateReadonly); 87 | 88 | var providerField = new FieldDescripter(ClassProviderName) 89 | .SetType(typeof(IObjectProvider)); 90 | providerField.SetAccess(AccessType.PrivateReadonly); 91 | 92 | var loggerField = new FieldDescripter(ClassLoggerName).SetType(typeof(ILogger)); 93 | loggerField.SetAccess(AccessType.PrivateReadonly); 94 | 95 | classDescripter.CreateFiled(binarySerializerField, providerField, loggerField) 96 | .AddUsing(typeof(IBinarySerializer).Namespace) 97 | .AddUsing(typeof(IObjectProvider).Namespace) 98 | .AddUsing(typeof(ILogger).Namespace); 99 | } 100 | 101 | private void generateConstructor(ref ClassDescripter classDescripter) 102 | { 103 | var constructor = new ConstructorDescripter(classDescripter.Name); 104 | constructor.SetAccess(AccessType.Public); 105 | 106 | var providerParameter = new ParameterDescripter(typeof(IObjectProvider).Name, "provider"); 107 | constructor.SetParams(providerParameter); 108 | 109 | var code = $@" 110 | {ClassProviderName} = provider; 111 | {ClassBinarySerializerName} = provider.GetObject(); 112 | {ClassLoggerName} = provider.GetObject>();"; 113 | constructor.SetCode(code); 114 | 115 | classDescripter.CreateConstructor(constructor); 116 | } 117 | #endregion 118 | 119 | #region method 120 | private MethodDescripter generateMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo) 121 | { 122 | var parameterType = methodInfo.ParseMethodParameter(); 123 | var returnType = methodInfo.ParseMethodReturnParameter(); 124 | var callType = Helper.AnalyseCallType(parameterType, returnType); 125 | 126 | classDescripter.AddUsing(parameterType.Namespace, returnType.Namespace, methodInfo.DeclaringType.Namespace); 127 | 128 | var method = new MethodDescripter("", classDescripter); 129 | switch (callType) 130 | { 131 | case CallType.Rpc: 132 | method = generateRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 133 | break; 134 | case CallType.ClientStreamRpc: 135 | method = generateClientStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 136 | break; 137 | case CallType.ServerStreamRpc: 138 | method = generateServerStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 139 | break; 140 | case CallType.DuplexStreamRpc: 141 | method = generateDuplexStreamRpcMethod(ref classDescripter, methodInfo, parameterType, returnType); 142 | break; 143 | default: 144 | throw new InvalidOperationException("Invalid Method definition!"); 145 | } 146 | 147 | var methodTypeAttribute = new AttributeDescripter(FakeCallTypeAttributeName, ((int)callType).ToString()); 148 | method.Attributes.Add(methodTypeAttribute); 149 | 150 | return method; 151 | } 152 | 153 | private MethodDescripter generateRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 154 | { 155 | var resultCode = Helper.GenerateAwaitResultCode(returnType); 156 | var requestCode = Helper.GenerateRequestCode(parameterType); 157 | var servicerName = methodInfo.DeclaringType.FullName; 158 | var methodName = methodInfo.Name; 159 | var returnCode = Helper.GenerateReturnCode(returnType); 160 | 161 | var method = generateMethodHead(ref classDescripter, methodInfo); 162 | method.Parameters.Add(new ParameterDescripter(parameterType.Name, "request")); 163 | method.Parameters.Add(new ParameterDescripter("ServerCallContext", "context")); 164 | method.AppendCode(genCallCode(servicerName, methodName, resultCode, requestCode, returnCode)); 165 | method.SetReturnType($"Task<{returnType.Name}>"); 166 | 167 | return method; 168 | } 169 | 170 | private MethodDescripter generateClientStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 171 | { 172 | var resultCode = Helper.GenerateAwaitResultCode(returnType); 173 | var servicerName = methodInfo.DeclaringType.FullName; 174 | var methodName = methodInfo.Name; 175 | var requestParameterType = parameterType.GenericTypeArguments[0]; 176 | var returnCode = Helper.GenerateReturnCode(returnType); 177 | 178 | var method = generateMethodHead(ref classDescripter, methodInfo); 179 | method.Parameters.Add(new ParameterDescripter($"IAsyncStreamReader<{requestParameterType.Name}>", "request")); 180 | method.Parameters.Add(new ParameterDescripter("ServerCallContext", "context")); 181 | method.AppendCode($"var streamReq = new AsyncRequestStream<{requestParameterType.Name}>(request);"); 182 | method.AppendCode(genCallCode(servicerName, methodName, resultCode, "streamReq", returnCode)); 183 | method.SetReturnType($"Task<{returnType.Name}>"); 184 | 185 | classDescripter.AddUsing(typeof(IAsyncStreamReader<>).Namespace); 186 | classDescripter.AddUsing(typeof(AsyncRequestStream<>).Namespace); 187 | return method; 188 | } 189 | 190 | private MethodDescripter generateServerStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 191 | { 192 | var requestCode = $"{Helper.GenerateRequestCode(parameterType)}, responseStream"; 193 | var servicerName = methodInfo.DeclaringType.FullName; 194 | var responseType = returnType.GenericTypeArguments[0]; 195 | var methodName = methodInfo.Name; 196 | 197 | var method = generateMethodHead(ref classDescripter, methodInfo); 198 | method.Parameters.Add(new ParameterDescripter(parameterType.Name, "request")); 199 | method.Parameters.Add(new ParameterDescripter($"IServerStreamWriter<{responseType.Name}>", "response")); 200 | method.Parameters.Add(new ParameterDescripter("ServerCallContext", "context")); 201 | method.AppendCode($"var responseStream = new AsyncResponseStream<{responseType.Name}>(response);"); 202 | method.AppendCode(genCallCode(servicerName, methodName, string.Empty, requestCode, string.Empty)); 203 | method.SetReturnType("Task"); 204 | 205 | classDescripter.AddUsing(typeof(IServerStreamWriter<>).Namespace); 206 | classDescripter.AddUsing(typeof(AsyncRequestStream<>).Namespace); 207 | return method; 208 | } 209 | 210 | private MethodDescripter generateDuplexStreamRpcMethod(ref ClassDescripter classDescripter, MethodInfo methodInfo, Type parameterType, Type returnType) 211 | { 212 | var requestParameterType = parameterType.GenericTypeArguments[0]; 213 | var servicerName = methodInfo.DeclaringType.FullName; 214 | var methodName = methodInfo.Name; 215 | var responseType = returnType.GenericTypeArguments[0]; 216 | var requestCode = "requestStream, responseStream"; 217 | 218 | var method = generateMethodHead(ref classDescripter, methodInfo); 219 | method.Parameters.Add(new ParameterDescripter($"IAsyncStreamReader<{requestParameterType.Name}>", "request")); 220 | method.Parameters.Add(new ParameterDescripter($"IServerStreamWriter<{responseType.Name}>", "response")); 221 | method.Parameters.Add(new ParameterDescripter("ServerCallContext", "context")); 222 | method.AppendCode($"var requestStream = new AsyncRequestStream<{requestParameterType.Name}>(request);"); 223 | method.AppendCode($"var responseStream = new AsyncResponseStream<{responseType.Name}>(response);"); 224 | method.AppendCode(genCallCode(servicerName, methodName, string.Empty, requestCode, string.Empty)); 225 | method.SetReturnType("Task"); 226 | 227 | classDescripter.AddUsing(typeof(IServerStreamWriter<>).Namespace); 228 | classDescripter.AddUsing(typeof(AsyncRequestStream<>).Namespace); 229 | return method; 230 | } 231 | 232 | private MethodDescripter generateMethodHead(ref ClassDescripter classDescripter, MethodInfo methodInfo) 233 | { 234 | var method = new MethodDescripter(methodInfo.Name, classDescripter, true); 235 | method.Access = AccessType.Public; 236 | return method; 237 | } 238 | 239 | private string genCallCode(string servicer, string method, string result, string request, string @return) 240 | { 241 | return $@"try 242 | {{ 243 | using(var scope = {ClassProviderName}.CreateScope()) 244 | {{ 245 | var servicer = scope.Provider.GetObject<{servicer}>() ?? throw new ArgumentNullException(""Not found servicer({servicer}) register!""); 246 | {result}await servicer.{method}({request}); 247 | {@return} 248 | }} 249 | }} 250 | catch(Exception ex) 251 | {{ 252 | _log.LogError(ex,$""Handler has an unknow error! Msg: {{ex.Message}}, Servicer: {servicer}, Method: {method}""); 253 | throw ex; 254 | }}"; 255 | } 256 | 257 | #endregion 258 | 259 | #region GrpcCallMethod 260 | private MethodDescripter generateBindServicesMethod(ref ClassDescripter classDescripter, Type servicerType) 261 | { 262 | var bindServicesMethod = new MethodDescripter("BindServices", classDescripter); 263 | bindServicesMethod.Access = AccessType.Public; 264 | bindServicesMethod.SetReturnType(typeof(ServerServiceDefinition)); 265 | 266 | bindServicesMethod.AppendCode(@"return ServerServiceDefinition.CreateBuilder()"); 267 | foreach (var method in classDescripter.Methods) 268 | { 269 | if (method.Attributes.Count == 0) 270 | continue; 271 | 272 | var fakeMethodTypeAttribute = method.Attributes.FirstOrDefault(p => p.Name == FakeCallTypeAttributeName); 273 | if (fakeMethodTypeAttribute == null) 274 | continue; 275 | 276 | var callType = (CallType)int.Parse(fakeMethodTypeAttribute.Parameters[0]); 277 | bindServicesMethod.AppendCode(generateBindServicesCode(classDescripter, method, callType, servicerType)); 278 | 279 | method.Attributes.Remove(fakeMethodTypeAttribute); 280 | } 281 | bindServicesMethod.AppendCode(@" .Build();"); 282 | 283 | return bindServicesMethod; 284 | } 285 | 286 | private string generateBindServicesCode(ClassDescripter @class, MethodDescripter method, CallType callType, Type servicerType) 287 | { 288 | var callInfo = getCallInfo(callType, method); 289 | callInfo.RequestType = callInfo.RequestType.Replace("IAsyncStreamReader<", "").Replace("Task<", "").Replace(">", ""); 290 | callInfo.ResponseType = callInfo.ResponseType.Replace("IServerStreamWriter<", "").Replace("Task<", "").Replace(">", ""); 291 | 292 | var code = new StringBuilder(); 293 | code.Append($@" .AddMethod(new Method<{callInfo.RequestType}, {callInfo.ResponseType}>( 294 | {callInfo.MethodType}, 295 | ""{@class.Namespace}.{servicerType.Name}"", 296 | ""{method.Name}"", 297 | new Marshaller<{callInfo.RequestType}>( 298 | {ClassBinarySerializerName}.Serialize, 299 | {ClassBinarySerializerName}.Deserialize<{callInfo.RequestType}> 300 | ), 301 | new Marshaller<{callInfo.ResponseType}>( 302 | {ClassBinarySerializerName}.Serialize, 303 | {ClassBinarySerializerName}.Deserialize<{callInfo.ResponseType}> 304 | )), 305 | {method.Name})"); 306 | 307 | // This is will be remove, It's use compatible async 308 | if (method.Name.EndsWith("Async")) 309 | { 310 | code.AppendLine(); 311 | code.Append($@" .AddMethod(new Method<{callInfo.RequestType}, {callInfo.ResponseType}>( 312 | {callInfo.MethodType}, 313 | ""{@class.Namespace}.{servicerType.Name}"", 314 | ""{method.Name.Replace("Async","")}"", 315 | new Marshaller<{callInfo.RequestType}>( 316 | {ClassBinarySerializerName}.Serialize, 317 | {ClassBinarySerializerName}.Deserialize<{callInfo.RequestType}> 318 | ), 319 | new Marshaller<{callInfo.ResponseType}>( 320 | {ClassBinarySerializerName}.Serialize, 321 | {ClassBinarySerializerName}.Deserialize<{callInfo.ResponseType}> 322 | )), 323 | {method.Name})"); 324 | } 325 | 326 | return code.ToString(); 327 | } 328 | 329 | private (String MethodType, string RequestType, string ResponseType) getCallInfo(CallType callType, MethodDescripter method) 330 | { 331 | switch (callType) 332 | { 333 | case CallType.Rpc: 334 | return ("MethodType.Unary", method.Parameters[0].Type, method.ReturnTypeStr); 335 | case CallType.ClientStreamRpc: 336 | return ("MethodType.ClientStreaming", method.Parameters[0].Type, method.ReturnTypeStr); 337 | case CallType.ServerStreamRpc: 338 | return ("MethodType.ServerStreaming", method.Parameters[0].Type, method.Parameters[1].Type); 339 | case CallType.DuplexStreamRpc: 340 | return ("MethodType.DuplexStreaming", method.Parameters[0].Type, method.Parameters[1].Type); 341 | default: 342 | throw new InvalidCastException("Invalid CallType"); 343 | } 344 | } 345 | #endregion 346 | } 347 | } 348 | --------------------------------------------------------------------------------