diff --git a/README.md b/README.md index 0beae0e18..ba578a742 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,150 @@ Here is example of what kind of filter can be stacked. GlobalFilter can attach to MagicOnionOptions. +ClientFilter +--- +MagicOnion client-filter is powerful feature to hook before-after invoke. It is useful than gRPC client interceptor. + +> Currently only suppots on Unary. + +```csharp +// you can attach in MagicOnionClient.Create. +var client = MagicOnionClient.Create(channel, new IClientFilter[] +{ + new LoggingFilter(), + new AppendHeaderFilter(), + new RetryFilter() +}); +``` + +You can create custom client-filter by implements `IClientFilter.SendAsync`. + +```csharp +public class IDemoFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + try + { + /* Before Request, context.MethodPath/CallOptions/Items, etc */ + + var response = await next(context); /* Call next filter or method body */ + + /* After Request, response.GetStatus/GetTrailers/GetResponseAs, etc */ + + return response; + } + catch (RpcException ex) + { + /* Get gRPC Error Response */ + throw; + } + catch (OperationCanceledException ex) + { + /* If canceled */ + throw; + } + catch (Exception ex) + { + /* Other Exception */ + throw; + } + finally + { + /* Common Finalize */ + } + } +} +``` + +Here is the sample filters, you can imagine what you can do. + +```csharp +public class AppendHeaderFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + // add the common header(like authentcation). + var header = context.CallOptions.Headers; + header.Add("x-foo", "abcdefg"); + header.Add("x-bar", "hijklmn"); + + return await next(context); + } +} + +public class LoggingFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + Console.WriteLine("Request Begin:" + context.MethodPath); // Debug.Log in Unity. + + var sw = Stopwatch.StartNew(); + var response = await next(context); + sw.Stop(); + + Console.WriteLine("Request Completed:" + context.MethodPath + ", Elapsed:" + sw.Elapsed.TotalMilliseconds + "ms"); + + return response; + } +} + +public class ResponseHandlingFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + var response = await next(context); + + if (context.MethodPath == "ICalc/Sum") + { + // You can cast response type. + var sumResult = await response.GetResponseAs(); + Console.WriteLine("Called Sum, Result:" + sumResult); + } + + return response; + } +} + +public class MockRequestFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + if (context.MethodPath == "ICalc/Sum") + { + // don't call next, return mock result. + return new ResponseContext(9999); + } + + return await next(context); + } +} + +public class RetryFilter : IClientFilter +{ + public async ValueTask SendAsync(RequestContext context, Func> next) + { + Exception lastException = null; + var retryCount = 0; + while (retryCount != 3) + { + try + { + // using same CallOptions so be careful to add duplicate headers or etc. + return await next(context); + } + catch (Exception ex) + { + lastException = ex; + } + retryCount++; + } + + throw new Exception("Retry failed", lastException); + } +} +``` + ServiceContext and Lifecycle --- Service/StreamingHub's method or `MagicOnionFilter` can access `this.Context` it is @@ -374,7 +518,7 @@ gRPC In( StreamingHub instance is shared while connecting so StreamingHub's field can use cache area of connection. -MagicOnionOption/Loggin +MagicOnionOption/Logging --- `MagicOnionOption` can pass to `MagicOnionEngine.BuildServerServiceDefinition(MagicOnionOptions option)`. diff --git a/sandbox/DynamicCodeDumper/Program.cs b/sandbox/DynamicCodeDumper/Program.cs index 97482a12e..196d17c2f 100644 --- a/sandbox/DynamicCodeDumper/Program.cs +++ b/sandbox/DynamicCodeDumper/Program.cs @@ -32,12 +32,49 @@ public interface ITestHub : IStreamingHub Task RetrunMoreArgument(int x, string y, double z); } + public interface ITestService : IService + { + UnaryResult Sum(int x, int y); + Task> Sum2(int x, int y); + + + UnaryResult Unary1(int x, int y, string z = "unknown"); + UnaryResult Unary2(MyRequest req); + UnaryResult Unary3(); + UnaryResult Unary4(); + UnaryResult Unary5(MyStructRequest req); + } + + public class MyRequest + { + } + + public class MyResponse + { + } + public struct MyStructRequest + { + } + + public struct MyStructResponse + { + } + public struct Nil + { + } + class Program { static void Main(string[] args) { //var _ = DynamicBroadcasterBuilder.BroadcasterType; //var a = MagicOnion.Server.Hubs.AssemblyHolder.Save(); + + // var _ = DynamicClientBuilder.ClientType; + //var c = MagicOnion.Client.DynamicClientAssemblyHolder.Save(); + //Verify(c); + + //var _ = MagicOnionClient.Create((Channel)null, null); //var __ = StreamingHubClientBuilder.ClientType; //var b = MagicOnion.Client.StreamingHubClientAssemblyHolder.Save(); diff --git a/src/MagicOnion.Client.Unity/Assembly-CSharp-Editor.csproj b/src/MagicOnion.Client.Unity/Assembly-CSharp-Editor.csproj index d280a493f..a165a07d7 100644 --- a/src/MagicOnion.Client.Unity/Assembly-CSharp-Editor.csproj +++ b/src/MagicOnion.Client.Unity/Assembly-CSharp-Editor.csproj @@ -62,19 +62,19 @@ - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.PackageManagerUI.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.PackageManagerUI.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll C:/Program Files/Unity/Hub/Editor/2018.3.3f1/Editor/Data/Managed/UnityEngine/UnityEngine.AIModule.dll @@ -311,31 +311,37 @@ C:/Program Files (x86)/Microsoft Visual Studio Tools for Unity/16.0/Editor/SyntaxTree.VisualStudio.Unity.Bridge.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Google.Protobuf/lib/net45/Google.Protobuf.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Google.Protobuf/lib/net45/Google.Protobuf.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/System.Interactive.Async/lib/net45/System.Interactive.Async.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/System.Interactive.Async/lib/net45/System.Interactive.Async.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.ads@2.0.8/Editor/UnityEditor.Advertisements.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.ads@2.0.8/Editor/UnityEditor.Advertisements.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.StandardEvents.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.StandardEvents.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Tracker.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Tracker.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.purchasing@2.0.3/Editor/UnityEditor.Purchasing.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.purchasing@2.0.3/Editor/UnityEditor.Purchasing.dll + + + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll + + + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll C:/Program Files/Unity/Hub/Editor/2018.3.3f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll diff --git a/src/MagicOnion.Client.Unity/Assembly-CSharp.csproj b/src/MagicOnion.Client.Unity/Assembly-CSharp.csproj index eb86c253d..353f5fd88 100644 --- a/src/MagicOnion.Client.Unity/Assembly-CSharp.csproj +++ b/src/MagicOnion.Client.Unity/Assembly-CSharp.csproj @@ -67,9 +67,11 @@ + + @@ -90,12 +92,7 @@ - - - - - @@ -192,19 +189,19 @@ - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.PackageManagerUI.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.PackageManagerUI.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.CollabProxy.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.TextMeshPro.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/ScriptAssemblies/Unity.Analytics.DataPrivacy.dll C:/Program Files/Unity/Hub/Editor/2018.3.3f1/Editor/Data/Managed/UnityEngine/UnityEngine.AIModule.dll @@ -408,25 +405,31 @@ C:/Program Files/Unity/Hub/Editor/2018.3.3f1/Editor/Data/UnityExtensions/Unity/UnitySpatialTracking/RuntimeEditor/UnityEngine.SpatialTracking.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Google.Protobuf/lib/net45/Google.Protobuf.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Google.Protobuf/lib/net45/Google.Protobuf.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/System.Interactive.Async/lib/net45/System.Interactive.Async.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Plugins/System.Interactive.Async/lib/net45/System.Interactive.Async.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Editor.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Editor.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.StandardEvents.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.StandardEvents.dll - C:/Git/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Tracker.dll + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Library/PackageCache/com.unity.analytics@3.2.2/Unity.Analytics.Tracker.dll + + + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll + + + C:/GitHubRepositories/MagicOnion/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll C:/Program Files/Unity/Hub/Editor/2018.3.3f1/Editor/Data/MonoBleedingEdge/lib/mono/4.7.1-api/mscorlib.dll diff --git a/src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity b/src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity new file mode 100644 index 000000000..8a67b7ffd Binary files /dev/null and b/src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity differ diff --git a/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.pdb.meta b/src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity.meta similarity index 58% rename from src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.pdb.meta rename to src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity.meta index 48019325c..cb5da15fc 100644 --- a/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core.Api/lib/net45/Grpc.Core.Api.pdb.meta +++ b/src/MagicOnion.Client.Unity/Assets/Scenes/New Scene.unity.meta @@ -1,7 +1,5 @@ fileFormatVersion: 2 -guid: bf384c9cae7a648c488af0193b3e74c0 -timeCreated: 1531219385 -licenseType: Free +guid: 7ce8540f04bcad54ea4cd2ae6cd1a08c DefaultImporter: externalObjects: {} userData: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/Editor/PackageExporter.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/Editor/PackageExporter.cs index fdf73d36f..909dff0e8 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/Editor/PackageExporter.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/Editor/PackageExporter.cs @@ -15,10 +15,16 @@ public static void Export() var path = Path.Combine(Application.dataPath, root); var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) - .Where(x => Path.GetExtension(x) == ".cs" || Path.GetExtension(x) == ".asmdef" || Path.GetExtension(x) == ".json") + .Where(x => Path.GetExtension(x) == ".cs" || Path.GetExtension(x) == ".asmdef" || Path.GetExtension(x) == ".json" || Path.GetExtension(x) == ".meta") .Select(x => "Assets" + x.Replace(Application.dataPath, "").Replace(@"\", "/")) .ToArray(); + var netStandardsAsset = Directory.EnumerateFiles(Path.Combine(Application.dataPath, "Scripts/NetStandardLibraries"), "*", SearchOption.TopDirectoryOnly) + .Select(x => "Assets" + x.Replace(Application.dataPath, "").Replace(@"\", "/")) + .ToArray(); + + assets = assets.Concat(netStandardsAsset).ToArray(); + UnityEngine.Debug.Log("Export below files" + Environment.NewLine + string.Join(Environment.NewLine, assets)); AssetDatabase.ExportPackage( diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs new file mode 100644 index 000000000..936a0d000 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs @@ -0,0 +1,182 @@ +using Grpc.Core; +using MessagePack; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace MagicOnion.Client +{ + public interface IClientFilter + { + ValueTask SendAsync(RequestContext context, Func> next); + } + + public abstract class RequestContext + { + public string MethodPath { get; } + public CallOptions CallOptions { get; } + public Type ResponseType { get; } + public abstract Type RequestType { get; } + + Dictionary items; + public IDictionary Items + { + get + { + if (items == null) + { + items = new Dictionary(); + } + return items; + } + } + + // internal use to avoid lambda capture. + internal MagicOnionClientBase Client { get; } + internal IClientFilter[] Filters { get; } + internal Func RequestMethod { get; } + + internal RequestContext(MagicOnionClientBase client, string methodPath, CallOptions callOptions, Type responseType, IClientFilter[] filters, Func requestMethod) + { + this.Client = client; + this.MethodPath = methodPath; + this.CallOptions = callOptions; + this.ResponseType = responseType; + this.Filters = filters; + this.RequestMethod = requestMethod; + } + } + + public class RequestContext : RequestContext + { + public T Request { get; } + public override Type RequestType => typeof(T); + + public RequestContext(T request, MagicOnionClientBase client, string methodPath, CallOptions callOptions, Type responseType, IClientFilter[] filters, Func requestMethod) + : base(client, methodPath, callOptions, responseType, filters, requestMethod) + { + this.Request = request; + } + } + + public abstract class ResponseContext : IDisposable + { + public abstract Task ResponseHeadersAsync { get; } + public abstract Status GetStatus(); + public abstract Metadata GetTrailers(); + public abstract void Dispose(); + public abstract Type ResponseType { get; } + + public abstract Task WaitResponseAsync(); + + public ResponseContext As() + { + return this as ResponseContext; + } + + public Task GetResponseAs() + { + var t = this as ResponseContext; + if (t == null) return Task.FromResult(default(T)); + + return t.ResponseAsync; + } + } + + public sealed class ResponseContext : ResponseContext + { + readonly AsyncUnaryCall inner; + readonly IFormatterResolver resolver; + readonly bool isMock; + bool deserialized; + + T responseObject; // cache value. + + // mock + readonly Metadata trailers; + readonly Metadata responseHeaders; + readonly Status status; + + public ResponseContext(AsyncUnaryCall inner, IFormatterResolver resolver) + { + this.isMock = false; + this.inner = inner; + this.resolver = resolver; + } + + public ResponseContext(T responseObject) + : this(responseObject, new Metadata(), new Metadata(), Status.DefaultSuccess) + { + } + + public ResponseContext(T responseObject, Metadata trailers, Metadata responseHeaders, Status status) + { + this.isMock = true; + this.responseObject = responseObject; + this.trailers = trailers; + this.responseHeaders = responseHeaders; + this.status = status; + } + + async Task Deserialize() + { + if (deserialized) + { + return responseObject; + } + else + { + var bytes = await inner.ResponseAsync.ConfigureAwait(false); + responseObject = LZ4MessagePackSerializer.Deserialize(bytes, resolver); + deserialized = true; + return responseObject; + } + } + + public Task ResponseAsync => !isMock ? Deserialize() : Task.FromResult(responseObject); + + public override async Task WaitResponseAsync() + { + await ResponseAsync; + return this; + } + + public override Type ResponseType => typeof(T); + + public override Task ResponseHeadersAsync => !isMock ? inner.ResponseHeadersAsync : Task.FromResult(responseHeaders); + + public override void Dispose() + { + if (!isMock) + { + inner.Dispose(); + } + } + + public override Status GetStatus() + { + return !isMock ? inner.GetStatus() : status; + } + + public override Metadata GetTrailers() + { + return !isMock ? inner.GetTrailers() : trailers; + } + + public ResponseContext WithNewResult(T result) + { + if (isMock) + { + return new ResponseContext(result, trailers, responseHeaders, status); + } + else + { + var newContext = new ResponseContext(inner, resolver); + newContext.deserialized = true; + newContext.responseObject = result; + return newContext; + } + } + } +} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs.meta similarity index 83% rename from src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs.meta rename to src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs.meta index d3b3ea268..043460c04 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs.meta +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/ClientFilter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 291e91a7a9253524e80e4bcfefb18d7a +guid: d0190394f8c415b4b84aa1b95b993369 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/DynamicClientBuilder.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/DynamicClientBuilder.cs index 21e13a56d..4b26eb2b1 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/DynamicClientBuilder.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/DynamicClientBuilder.cs @@ -12,7 +12,12 @@ namespace MagicOnion.Client { - internal static class DynamicClientAssemblyHolder +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + static class DynamicClientAssemblyHolder { public const string ModuleName = "MagicOnion.Client.DynamicClient"; @@ -34,7 +39,13 @@ public static AssemblyBuilder Save() #endif } - internal static class DynamicClientBuilder +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + static class DynamicClientBuilder + where T : IService { public static readonly Type ClientType; static readonly Type bytesMethod = typeof(Method<,>).MakeGenericType(new[] { typeof(byte[]), typeof(byte[]) }); @@ -130,11 +141,49 @@ static void DefineStaticConstructor(TypeBuilder typeBuilder, Type interfaceType, il.Emit(OpCodes.Ldsfld, throughMarshaller); il.Emit(OpCodes.Newobj, bytesMethod.GetConstructors()[0]); il.Emit(OpCodes.Stsfld, def.FieldMethod); + + if (def.MethodType == MethodType.Unary) + { + DefineUnaryRequestDelegate(il, typeBuilder, interfaceType, def); + } } il.Emit(OpCodes.Ret); } + static void DefineUnaryRequestDelegate(ILGenerator staticContructorGenerator, TypeBuilder typeBuilder, Type interfaceType, MethodDefinition definition) + { + // static ResponseContext _Method(RequestContext context); + MethodBuilder method; + { + method = typeBuilder.DefineMethod("_" + definition.MethodInfo.Name, MethodAttributes.Private | MethodAttributes.Static, + typeof(ResponseContext), + new[] { typeof(RequestContext) }); + var il = method.GetILGenerator(); + + // CreateResponseContext(Context, Method); + var createMethod = typeof(MagicOnionClientBase) + .GetMethod("CreateResponseContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) + .MakeGenericMethod(definition.RequestType, definition.ResponseType); + + il.Emit(OpCodes.Ldarg_0); // context + il.Emit(OpCodes.Ldsfld, definition.FieldMethod); // method + il.Emit(OpCodes.Call, createMethod); + il.Emit(OpCodes.Ret); + } + + // static readonly Func methodDelegate = _Method; + { + definition.UnaryRequestDelegate = typeBuilder.DefineField(definition.MethodInfo.Name + "Delegate", typeof(Func), FieldAttributes.Private | FieldAttributes.Static); + + var il = staticContructorGenerator; + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Ldftn, method); + il.Emit(OpCodes.Newobj, typeof(Func).GetConstructors()[0]); + il.Emit(OpCodes.Stsfld, definition.UnaryRequestDelegate); + } + } + static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinition[] definitions) { ConstructorInfo emptyCtor; @@ -150,15 +199,16 @@ static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinit emptyCtor = ctor; } - // .ctor(CallInvoker, IFormatterResolver):base(callInvoker, resolver) + // .ctor(CallInvoker, IFormatterResolver, IClientFilter[]):base(callInvoker, resolver, clientFilters) { - var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(CallInvoker), typeof(IFormatterResolver) }); + var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(CallInvoker), typeof(IFormatterResolver), typeof(IClientFilter[]) }); var il = ctor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(CallInvoker), typeof(IFormatterResolver) }, null)); + il.Emit(OpCodes.Ldarg_3); + il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(CallInvoker), typeof(IFormatterResolver), typeof(IClientFilter[]) }, null)); il.Emit(OpCodes.Ret); } @@ -167,11 +217,13 @@ static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinit static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDefinition[] definitions, ConstructorInfo emptyCtor) { + var filedHolderType = typeof(MagicOnionClientBase); var baseType = typeof(MagicOnionClientBase<>).MakeGenericType(interfaceType); - var hostField = baseType.GetField("host", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var optionField = baseType.GetField("option", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var invokerField = baseType.GetField("callInvoker", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var resolverField = baseType.GetField("resolver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var hostField = filedHolderType.GetField("host", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var optionField = filedHolderType.GetField("option", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var invokerField = filedHolderType.GetField("callInvoker", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var resolverField = filedHolderType.GetField("resolver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var filtersField = filedHolderType.GetField("filters", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // Clone { @@ -202,6 +254,11 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDef il.Emit(OpCodes.Ldfld, resolverField); il.Emit(OpCodes.Stfld, resolverField); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, filtersField); + il.Emit(OpCodes.Stfld, filtersField); + il.Emit(OpCodes.Ret); } // Overrides @@ -281,81 +338,101 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDef switch (def.MethodType) { case MethodType.Unary: - case MethodType.ServerStreaming: - il.DeclareLocal(typeof(byte[])); // request - if (def.MethodType == MethodType.Unary) { - il.DeclareLocal(typeof(AsyncUnaryCall)); // callResult + // base.InvokeAsync/InvokeTaskAsync(string path, TRequest request, Func requestMethod) + + // this. + il.Emit(OpCodes.Ldarg_0); + + // path + il.Emit(OpCodes.Ldstr, def.Path); + + // create request + for (int j = 0; j < parameters.Length; j++) + { + il.Emit(OpCodes.Ldarg, j + 1); + } + if (parameters.Length == 0) + { + // use empty byte[0] + il.Emit(OpCodes.Ldsfld, nilBytes); + } + else if (parameters.Length == 1) + { + // already loaded parameter. + } + else + { + // call new DynamicArgumentTuple + il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + } + + // requestMethod + il.Emit(OpCodes.Ldsfld, def.UnaryRequestDelegate); + + // InvokeAsync/InvokeTaskAsync + var invokeMethod = def.ResponseIsTask + ? baseType.GetMethod("InvokeTaskAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance) + : baseType.GetMethod("InvokeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance); + invokeMethod = invokeMethod.MakeGenericMethod(def.RequestType, def.ResponseType); + il.Emit(OpCodes.Callvirt, invokeMethod); } - else + + break; + case MethodType.ServerStreaming: { + il.DeclareLocal(typeof(byte[])); // request il.DeclareLocal(typeof(AsyncServerStreamingCall)); - } - // create request - for (int j = 0; j < parameters.Length; j++) - { - il.Emit(OpCodes.Ldarg, j + 1); - } - if (parameters.Length == 0) - { - // use empty byte[0] - il.Emit(OpCodes.Ldsfld, nilBytes); - } - else if (parameters.Length == 1) - { - // already loaded parameter. + // create request + for (int j = 0; j < parameters.Length; j++) + { + il.Emit(OpCodes.Ldarg, j + 1); + } + if (parameters.Length == 0) + { + // use empty byte[0] + il.Emit(OpCodes.Ldsfld, nilBytes); + } + else if (parameters.Length == 1) + { + // already loaded parameter. + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); + } + else + { + // call new DynamicArgumentTuple + il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); + } + il.Emit(OpCodes.Stloc_0); + + // create ***Result il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); - } - else - { - // call new DynamicArgumentTuple - il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + il.Emit(OpCodes.Ldfld, invokerField); + il.Emit(OpCodes.Ldsfld, def.FieldMethod); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); - } - il.Emit(OpCodes.Stloc_0); - - // create ***Result - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, invokerField); - il.Emit(OpCodes.Ldsfld, def.FieldMethod); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, hostField); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, optionField); - il.Emit(OpCodes.Ldloc_0); - if (def.MethodType == MethodType.Unary) - { - il.Emit(OpCodes.Callvirt, typeof(CallInvoker).GetMethod("AsyncUnaryCall").MakeGenericMethod(typeof(byte[]), typeof(byte[]))); - } - else - { + il.Emit(OpCodes.Ldfld, hostField); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, optionField); + il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, typeof(CallInvoker).GetMethod("AsyncServerStreamingCall").MakeGenericMethod(typeof(byte[]), typeof(byte[]))); - } - il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Stloc_1); - // create return result - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - Type resultType; - if (def.MethodType == MethodType.Unary) - { - resultType = typeof(UnaryResult<>).MakeGenericType(def.ResponseType); - il.Emit(OpCodes.Newobj, resultType.GetConstructors().OrderBy(x => x.GetParameters().Length).Last()); - } - else - { - resultType = typeof(ServerStreamingResult<>).MakeGenericType(def.ResponseType); + // create return result + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + var resultType = typeof(ServerStreamingResult<>).MakeGenericType(def.ResponseType); il.Emit(OpCodes.Newobj, resultType.GetConstructors()[0]); - } - if (def.ResponseIsTask) - { - il.Emit(OpCodes.Call, typeof(Task).GetMethod("FromResult").MakeGenericMethod(resultType)); + if (def.ResponseIsTask) + { + il.Emit(OpCodes.Call, typeof(Task).GetMethod("FromResult").MakeGenericMethod(resultType)); + } } break; case MethodType.ClientStreaming: @@ -481,6 +558,9 @@ class MethodDefinition public FieldInfo FieldMethod; public Type RequestType; public Type ResponseType; + + // unary only, set after define static fields + public FieldInfo UnaryRequestDelegate; } } } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs index cdb829706..1b55c9653 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs @@ -1,6 +1,7 @@ using Grpc.Core; using MagicOnion.Server.EmbeddedServices; using MessagePack; +using System; using System.Threading.Tasks; #if !NON_UNITY @@ -38,7 +39,7 @@ public HeartbeatClient(Channel channel) } public HeartbeatClient(CallInvoker callInvoker) - : base(callInvoker, null) + : base(callInvoker, null, Array.Empty()) { } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/PingClient.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/PingClient.cs index d2249d4bb..d2440a7f1 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/PingClient.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/EmbeddedServices/PingClient.cs @@ -1,6 +1,7 @@ using Grpc.Core; using MagicOnion.Server.EmbeddedServices; using MessagePack; +using System; #if !NON_UNITY @@ -37,7 +38,7 @@ public PingClient(Channel channel) } public PingClient(CallInvoker callInvoker) - : base(callInvoker, null) + : base(callInvoker, null, Array.Empty()) { } @@ -53,8 +54,13 @@ protected override MagicOnionClientBase Clone() public UnaryResult Ping() { - var __callResult = callInvoker.AsyncUnaryCall(Method, base.host, base.option, MagicOnionMarshallers.UnsafeNilBytes); - return new UnaryResult(__callResult, MessagePack.Resolvers.BuiltinResolver.Instance); + return InvokeAsync("IMagicOnionEmbeddedPing/Ping", MagicOnionMarshallers.UnsafeNilBytes, __ctx => + { + var __self = (PingClient)__ctx.Client; + var __request = MagicOnionMarshallers.UnsafeNilBytes; + var __callResult = __self.callInvoker.AsyncUnaryCall(PingClient.Method, __self.host, __ctx.CallOptions, __request); + return new ResponseContext(__callResult, MessagePack.Resolvers.BuiltinResolver.Instance); + }); } } } \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs new file mode 100644 index 000000000..870155e54 --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs @@ -0,0 +1,158 @@ +using System.Threading.Tasks; + +namespace MagicOnion.Client +{ + internal static class InterceptInvokeHelper + { + public static ValueTask InvokeWithFilter(RequestContext context) + { + switch (context.Filters.Length) + { + case 0: + return new ValueTask(context.RequestMethod(context)); + case 1: + return InvokeWithFilter1(context); + case 2: + return InvokeWithFilter2(context); + case 3: + return InvokeWithFilter3(context); + case 4: + return InvokeWithFilter4(context); + case 5: + return InvokeWithFilter5(context); + case 6: + return InvokeWithFilter6(context); + case 7: + return InvokeWithFilter7(context); + case 8: + return InvokeWithFilter8(context); + case 9: + return InvokeWithFilter9(context); + case 10: + return InvokeWithFilter10(context); + default: + return InvokeRecursive(-1, context); + } + } + + static ValueTask InvokeWithFilter1(RequestContext context) + { + return context.Filters[0].SendAsync(context, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())); + } + + static ValueTask InvokeWithFilter2(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))); + } + + static ValueTask InvokeWithFilter3(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))); + } + + static ValueTask InvokeWithFilter4(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))); + } + + static ValueTask InvokeWithFilter5(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))); + } + + static ValueTask InvokeWithFilter6(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))); + } + + static ValueTask InvokeWithFilter7(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))))); + } + + static ValueTask InvokeWithFilter8(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))))); + } + + static ValueTask InvokeWithFilter9(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + x8 => x8.Filters[8].SendAsync(x8, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))))))); + } + + static ValueTask InvokeWithFilter10(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + x8 => x8.Filters[8].SendAsync(x8, + x9 => x9.Filters[9].SendAsync(x9, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))))))); + } + + // for invoke N filters(slow path). + + static ValueTask InvokeRecursive(int index, RequestContext context) + { + index += 1; // start from -1 + if (index != context.Filters.Length) + { + return context.Filters[index].SendAsync(context, ctx => InvokeRecursive(index, ctx)); + } + else + { + return new ValueTask(context.RequestMethod(context).WaitResponseAsync()); + } + } + } +} \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs.meta similarity index 69% rename from src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs.meta rename to src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs.meta index ece987b9b..e1c983c5b 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs.meta +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/InterceptInvokeHelper.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: 5e18c27861d1f334e9e56dfff98f56a1 -timeCreated: 1490604045 -licenseType: Pro +guid: 4a428b894e3ddac4a80496efd076633c MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClient.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClient.cs index 7d5356bcb..b65b28f8b 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClient.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClient.cs @@ -6,26 +6,46 @@ namespace MagicOnion.Client { public static class MagicOnionClient { + static readonly IClientFilter[] emptyFilters = Array.Empty(); + public static T Create(Channel channel) where T : IService { - return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver); + return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver, emptyFilters); } public static T Create(CallInvoker invoker) where T : IService { - return Create(invoker, MessagePackSerializer.DefaultResolver); + return Create(invoker, MessagePackSerializer.DefaultResolver, emptyFilters); + } + + public static T Create(Channel channel, IClientFilter[] clientFilters) + where T : IService + { + return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver, clientFilters); + } + + public static T Create(CallInvoker invoker, IClientFilter[] clientFilters) + where T : IService + { + return Create(invoker, MessagePackSerializer.DefaultResolver, clientFilters); } public static T Create(Channel channel, IFormatterResolver resolver) where T : IService { - return Create(new DefaultCallInvoker(channel), resolver); + return Create(new DefaultCallInvoker(channel), resolver, emptyFilters); } public static T Create(CallInvoker invoker, IFormatterResolver resolver) where T : IService + { + return Create(invoker, resolver, emptyFilters); + } + + public static T Create(CallInvoker invoker, IFormatterResolver resolver, IClientFilter[] clientFilters) + where T : IService { if (invoker == null) throw new ArgumentNullException(nameof(invoker)); @@ -36,12 +56,12 @@ public static T Create(CallInvoker invoker, IFormatterResolver resolver) throw new InvalidOperationException("Does not registered client factory, dynamic code generation is not supported on IL2CPP. Please use code generator(moc)."); #else var t = DynamicClientBuilder.ClientType; - return (T)Activator.CreateInstance(t, invoker, resolver); + return (T)Activator.CreateInstance(t, invoker, resolver, clientFilters); #endif } else { - return ctor(invoker, resolver); + return ctor(invoker, resolver, clientFilters); } } } @@ -49,9 +69,9 @@ public static T Create(CallInvoker invoker, IFormatterResolver resolver) public static class MagicOnionClientRegistry where T : IService { - public static Func consturtor; + public static Func consturtor; - public static void Register(Func ctor) + public static void Register(Func ctor) { consturtor = ctor; } diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClientBase.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClientBase.cs index edfbccf21..43089791a 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClientBase.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Client/MagicOnionClientBase.cs @@ -1,27 +1,95 @@ using Grpc.Core; -using MagicOnion.Server; using MessagePack; using System; using System.Threading; +using System.Threading.Tasks; namespace MagicOnion.Client { - public abstract class MagicOnionClientBase where T : IService + public abstract class MagicOnionClientBase { protected string host; protected CallOptions option; protected CallInvoker callInvoker; protected IFormatterResolver resolver; + protected IClientFilter[] filters; - protected MagicOnionClientBase() + static protected ResponseContext CreateResponseContext(RequestContext context, Method method) { + var self = context.Client; + var callResult = self.callInvoker.AsyncUnaryCall(method, self.host, context.CallOptions, MagicOnionMarshallers.UnsafeNilBytes); + return new ResponseContext(callResult, self.resolver); + } + + static protected ResponseContext CreateResponseContext(RequestContext context, Method method) + { + var self = context.Client; + var message = LZ4MessagePackSerializer.Serialize(((RequestContext)context).Request, self.resolver); + var callResult = self.callInvoker.AsyncUnaryCall(method, self.host, context.CallOptions, message); + return new ResponseContext(callResult, self.resolver); + } + } + public abstract class MagicOnionClientBase : MagicOnionClientBase + where T : IService + { + protected MagicOnionClientBase() + { } - protected MagicOnionClientBase(CallInvoker callInvoker, IFormatterResolver resolver) + protected MagicOnionClientBase(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters) { this.callInvoker = callInvoker; this.resolver = resolver; + this.filters = filters; + } + + protected UnaryResult InvokeAsync(string path, TRequest request, Func requestMethod) + { + var future = InvokeAsyncCore(path, request, requestMethod); + return new UnaryResult(future); + } + + async Task> InvokeAsyncCore(string path, TRequest request, Func requestMethod) + { + if (this.option.Headers == null && filters.Length != 0) + { + // always creating new Metadata is bad manner for performance + this.option = this.option.WithHeaders(new Metadata()); + } + + var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); + var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); + var result = response as ResponseContext; + if (result != null) + { + return result; + } + else + { + throw new InvalidOperationException("ResponseContext is null."); + } + } + + protected async Task> InvokeTaskAsync(string path, TRequest request, Func requestMethod) + { + if (this.option.Headers == null && filters.Length != 0) + { + // always creating new Metadata is bad manner for performance + this.option = this.option.WithHeaders(new Metadata()); + } + + var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); + var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); + var result = response as ResponseContext; + if (result != null) + { + return new UnaryResult(Task.FromResult(result)); + } + else + { + throw new InvalidOperationException("ResponseContext is null."); + } } protected abstract MagicOnionClientBase Clone(); diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/UnaryResult.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/UnaryResult.cs index 28b05d41b..2c00d4946 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/UnaryResult.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/UnaryResult.cs @@ -1,6 +1,7 @@ using Grpc.Core; +using MagicOnion.Client; using MagicOnion.CompilerServices; // require this using in AsyncMethodBuilder -using MessagePack; +using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -18,16 +19,14 @@ public struct UnaryResult internal readonly TResponse rawValue; // internal internal readonly Task rawTaskValue; // internal - readonly AsyncUnaryCall inner; - readonly IFormatterResolver resolver; + readonly Task> response; public UnaryResult(TResponse rawValue) { this.hasRawValue = true; this.rawValue = rawValue; this.rawTaskValue = null; - this.inner = null; - this.resolver = null; + this.response = null; } public UnaryResult(Task rawTaskValue) @@ -35,23 +34,15 @@ public UnaryResult(Task rawTaskValue) this.hasRawValue = true; this.rawValue = default(TResponse); this.rawTaskValue = rawTaskValue; - this.inner = null; - this.resolver = null; + this.response = null; } - public UnaryResult(AsyncUnaryCall inner, IFormatterResolver resolver) + public UnaryResult(Task> response) { this.hasRawValue = false; this.rawValue = default(TResponse); this.rawTaskValue = null; - this.inner = inner; - this.resolver = resolver; - } - - async Task Deserialize() - { - var bytes = await inner.ResponseAsync.ConfigureAwait(false); - return LZ4MessagePackSerializer.Deserialize(bytes, resolver); + this.response = response; } /// @@ -63,7 +54,7 @@ public Task ResponseAsync { if (!hasRawValue) { - return Deserialize(); + return UnwrapResponse(); } else if (rawTaskValue != null) { @@ -83,10 +74,44 @@ public Task ResponseHeadersAsync { get { - return inner.ResponseHeadersAsync; + return UnwrapResponseHeaders(); + } + } + + async Task UnwrapResponse() + { + var ctx = await response.ConfigureAwait(false); + return await ctx.ResponseAsync.ConfigureAwait(false); + } + + async Task UnwrapResponseHeaders() + { + var ctx = await response.ConfigureAwait(false); + return await ctx.ResponseHeadersAsync.ConfigureAwait(false); + } + + async void UnwrapDispose() + { + try + { + var ctx = await response.ConfigureAwait(false); + ctx.Dispose(); + } + catch + { } } + ResponseContext TryUnwrap() + { + if (!response.IsCompleted) + { + throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); + } + + return response.Result; + } + /// /// Allows awaiting this object directly. /// @@ -101,7 +126,7 @@ public TaskAwaiter GetAwaiter() /// public Status GetStatus() { - return inner.GetStatus(); + return TryUnwrap().GetStatus(); } /// @@ -110,7 +135,7 @@ public Status GetStatus() /// public Metadata GetTrailers() { - return inner.GetTrailers(); + return TryUnwrap().GetTrailers(); } /// @@ -125,7 +150,14 @@ public Metadata GetTrailers() /// public void Dispose() { - inner.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } } \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs deleted file mode 100644 index bd06a0204..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)) - -namespace System.Runtime.CompilerServices -{ - public sealed class AsyncMethodBuilderAttribute : Attribute - { - public Type BuilderType { get; } - - public AsyncMethodBuilderAttribute(Type builderType) - { - BuilderType = builderType; - } - } -} - -#endif \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs.meta deleted file mode 100644 index 6fb79912f..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncMethodBuilderAttribute.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: ae14fd23ed18c984e8959b1d12824b67 -timeCreated: 1544664808 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs deleted file mode 100644 index 4a916f695..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/AsyncStreamReaderExtensions.cs +++ /dev/null @@ -1,298 +0,0 @@ -//using Grpc.Core; -//using System; -//using System.Threading; -//using UniRx; - -//namespace MagicOnion -//{ -// public static class AsyncStreamReaderExtensions -// { -// public static IDisposable Subscribe(this IAsyncStreamReader stream, Action onNext, bool observeOnMainThread = true, IDisposable streamingResult = null) -// { -// if (observeOnMainThread) -// { -// return AsObservable(stream, observeOnMainThread, streamingResult).Subscribe(onNext); -// } - -// var subscription = ForEachAsync(stream, onNext).Subscribe(); - -// if (streamingResult == null) return subscription; - -// return StableCompositeDisposable.Create(subscription, streamingResult); -// } - -// public static IDisposable Subscribe(this IAsyncStreamReader stream, IObserver observer, bool observeOnMainThread = true, IDisposable streamingResult = null) -// { -// var subscription = AsObservable(stream, observeOnMainThread).Subscribe(observer); - -// if (streamingResult == null) return subscription; -// return StableCompositeDisposable.Create(subscription, streamingResult); -// } - -// public static IObservable ForEachAsync(this IAsyncStreamReader stream, Action action) -// { -// return Observable.CreateWithState, Action>>(Tuple.Create(stream, action), (state0, observer) => -// { -// var disp = new MultipleAssignmentDisposable(); - -// var worker = new AsyncStreamReaderForEachAsync_(disp, state0.Item1, state0.Item2, observer); -// worker.ConsumeNext(); - -// return disp; -// }); -// } - -// class AsyncStreamReaderForEachAsync_ : IObserver -// { -// readonly MultipleAssignmentDisposable disp; -// readonly IAsyncStreamReader stream; -// readonly Action action; -// readonly IObserver rootObserver; - -// int isStopped = 0; - -// public AsyncStreamReaderForEachAsync_(MultipleAssignmentDisposable disp, IAsyncStreamReader stream, Action action, IObserver rootObserver) -// { -// this.disp = disp; -// this.stream = stream; -// this.action = action; -// this.rootObserver = rootObserver; -// } - -// public void ConsumeNext() -// { -// try -// { -// disp.Disposable = stream.MoveNext().Subscribe(this); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// rootObserver.OnError(ex); -// } -// } - -// public void OnNext(bool value) -// { -// if (isStopped == 0) -// { -// if (value == true) -// { -// try -// { -// action(stream.Current); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// rootObserver.OnError(ex); -// return; -// } - -// ConsumeNext(); // recursive next -// } -// else -// { -// rootObserver.OnCompleted(); -// } -// } -// } - -// public void OnError(Exception error) -// { -// if (Interlocked.Increment(ref isStopped) == 1) -// { -// stream.Dispose(); -// rootObserver.OnError(error); -// } -// } - -// public void OnCompleted() -// { -// // re-use observer. -// } -// } - -// public static IObservable ForEachAsync(this IAsyncStreamReader stream, Func> asyncAction) -// { -// return Observable.CreateWithState, Func>>>(Tuple.Create(stream, asyncAction), (state0, observer) => -// { -// var disp = new MultipleAssignmentDisposable(); - -// var worker = new AsyncStreamReaderForEachAsync__(disp, state0.Item1, state0.Item2, observer); -// worker.ConsumeNext(); - -// return disp; -// }); -// } - -// class AsyncStreamReaderForEachAsync__ : IObserver -// { -// readonly MultipleAssignmentDisposable disp; -// readonly IAsyncStreamReader stream; -// readonly Func> asyncAction; -// readonly IObserver rootObserver; - -// int isStopped = 0; - -// public AsyncStreamReaderForEachAsync__(MultipleAssignmentDisposable disp, IAsyncStreamReader stream, Func> asyncAction, IObserver rootObserver) -// { -// this.disp = disp; -// this.stream = stream; -// this.asyncAction = asyncAction; -// this.rootObserver = rootObserver; -// } - -// public void ConsumeNext() -// { -// try -// { -// disp.Disposable = stream.MoveNext().Subscribe(this); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// rootObserver.OnError(ex); -// } -// } - -// public void OnNext(bool value) -// { -// if (isStopped == 0) -// { -// if (value == true) -// { -// try -// { -// this.disp.Disposable = asyncAction(stream.Current) -// .Subscribe(_ => -// { -// ConsumeNext(); -// }, ex => OnError(ex)); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// rootObserver.OnError(ex); -// return; -// } - -// ConsumeNext(); // recursive next -// } -// else -// { -// rootObserver.OnCompleted(); -// } -// } -// } - -// public void OnError(Exception error) -// { -// if (Interlocked.Increment(ref isStopped) == 1) -// { -// stream.Dispose(); -// rootObserver.OnError(error); -// } -// } - -// public void OnCompleted() -// { -// } -// } - -// public static IObservable AsObservable(this IAsyncStreamReader stream, bool observeOnMainThread = true, IDisposable streamingResult = null) -// { -// var seq = Observable.CreateWithState, IDisposable>>(Tuple.Create(stream, streamingResult), (state, observer) => -// { -// var disp = new MultipleAssignmentDisposable(); -// var b = new AsyncStreamReaderAsObservable_(disp, state.Item1, observer, state.Item2); -// b.ConsumeNext(); - -// if (state.Item2 == null) -// { -// return disp; -// } -// else -// { -// return StableCompositeDisposable.Create(disp, state.Item2); -// } -// }); - -// return (observeOnMainThread) ? seq.ObserveOnMainThread() : seq; -// } - -// class AsyncStreamReaderAsObservable_ : IObserver -// { -// readonly MultipleAssignmentDisposable disp; -// readonly IAsyncStreamReader stream; -// readonly IObserver rootObserver; -// IDisposable streamingResult; - -// int isStopped = 0; - -// public AsyncStreamReaderAsObservable_(MultipleAssignmentDisposable disp, IAsyncStreamReader stream, IObserver rootObserver, IDisposable streamingResult) -// { -// this.disp = disp; -// this.stream = stream; -// this.rootObserver = rootObserver; -// this.streamingResult = streamingResult ?? Disposable.Empty; -// } - -// public void ConsumeNext() -// { -// try -// { -// disp.Disposable = stream.MoveNext().Subscribe(this); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// streamingResult.Dispose(); -// rootObserver.OnError(ex); -// } -// } - -// public void OnNext(bool value) -// { -// if (isStopped == 0) -// { -// if (value == true) -// { -// try -// { -// rootObserver.OnNext(stream.Current); -// } -// catch (Exception ex) -// { -// stream.Dispose(); -// streamingResult.Dispose(); -// rootObserver.OnError(ex); -// return; -// } - -// ConsumeNext(); // recursive next -// } -// else -// { -// rootObserver.OnCompleted(); -// } -// } -// } - -// public void OnError(Exception error) -// { -// if (Interlocked.Increment(ref isStopped) == 1) -// { -// stream.Dispose(); -// streamingResult.Dispose(); -// rootObserver.OnError(error); -// } -// } - -// public void OnCompleted() -// { -// // re-use observer. -// } -// } -// } -//} diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs deleted file mode 100644 index 229e19a43..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs +++ /dev/null @@ -1,39 +0,0 @@ -#if UNITY_EDITOR -using UnityEditor; - -namespace MagicOnion -{ - //[InitializeOnLoad] - //public class CompileStopperDuringPlayMode - //{ - // static bool isStopped = false; - - // static CompileStopperDuringPlayMode() - // { - // EditorApplication.update += OnEditorUpdate; - // } - - // static void OnEditorUpdate() - // { - // if (!isStopped - // && EditorApplication.isCompiling - // && EditorApplication.isPlaying) - // { - // EditorApplication.LockReloadAssemblies(); - // EditorApplication.playmodeStateChanged += PlayModeChanged; - // isStopped = true; - // } - // } - - // static void PlayModeChanged() - // { - // if (EditorApplication.isPlaying) - // return; - - // EditorApplication.UnlockReloadAssemblies(); - // EditorApplication.playmodeStateChanged -= PlayModeChanged; - // isStopped = false; - // } - //} -} -#endif \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs.meta deleted file mode 100644 index 7d7c29cba..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/CompileStopperDuringPlayMode.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: f09ae5a1c2ff04a1186ae8f9e0021612 -timeCreated: 1505189122 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs deleted file mode 100644 index 6c43c9b06..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs +++ /dev/null @@ -1,183 +0,0 @@ -#if UNITY_EDITOR - -using Grpc.Core; -using Grpc.Core.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MagicOnion -{ - //public class EditorWindowSupportsCallInvoker : CallInvoker - //{ - // readonly CallInvoker unaryInvoker; - // readonly Channel channel; - - // public EditorWindowSupportsCallInvoker(Channel channel) - // { - // this.channel = channel; - // this.unaryInvoker = null; - // } - - // public EditorWindowSupportsCallInvoker(CallInvoker unaryInvoker, Channel channel) - // { - // this.channel = channel; - // this.unaryInvoker = unaryInvoker; - // } - - // /// - // /// Invokes a simple remote call asynchronously. - // /// - // public override AsyncUnaryCall AsyncUnaryCall(Method method, string host, CallOptions options, TRequest request) - // { - // if (unaryInvoker == null) - // { - // var call = CreateCall(method, host, options); - // return Calls.AsyncUnaryCall(call, request); - // } - // else - // { - // return unaryInvoker.AsyncUnaryCall(method, host, options, request); - // } - // } - - // /// - // /// Invokes a server streaming call asynchronously. - // /// In server streaming scenario, client sends on request and server responds with a stream of responses. - // /// - // public override AsyncServerStreamingCall AsyncServerStreamingCall(Method method, string host, CallOptions options, TRequest request) - // { - // var id = MagicOnionWindow.AddSubscription(channel, method.FullName); - - // var call = CreateCall(method, host, options); - // return CustomCalls.AsyncServerStreamingCall(call, request, () => MagicOnionWindow.RemoveSubscription(id), id); - // } - - // /// - // /// Invokes a client streaming call asynchronously. - // /// In client streaming scenario, client sends a stream of requests and server responds with a single response. - // /// - // public override AsyncClientStreamingCall AsyncClientStreamingCall(Method method, string host, CallOptions options) - // { - // var id = MagicOnionWindow.AddSubscription(channel, method.FullName); - - // var call = CreateCall(method, host, options); - // return CustomCalls.AsyncClientStreamingCall(call, () => MagicOnionWindow.RemoveSubscription(id)); - // } - - // /// - // /// Invokes a duplex streaming call asynchronously. - // /// In duplex streaming scenario, client sends a stream of requests and server responds with a stream of responses. - // /// The response stream is completely independent and both side can be sending messages at the same time. - // /// - // public override AsyncDuplexStreamingCall AsyncDuplexStreamingCall(Method method, string host, CallOptions options) - // { - // var id = MagicOnionWindow.AddSubscription(channel, method.FullName); - - // var call = CreateCall(method, host, options); - // return CustomCalls.AsyncDuplexStreamingCall(call, () => MagicOnionWindow.RemoveSubscription(id)); - // } - - // protected virtual CallInvocationDetails CreateCall(Method method, string host, CallOptions options) - // where TRequest : class - // where TResponse : class - // { - // return new CallInvocationDetails(channel, method, host, options); - // } - //} - - //public static class CustomCalls - //{ - // public static AsyncUnaryCall AsyncUnaryCall(CallInvocationDetails call, TRequest req, Action customDisposeAction) - // where TRequest : class - // where TResponse : class - // { - // var asyncCall = new AsyncCall(call); - // var asyncResult = asyncCall.UnaryCallAsync(req); - - // var token = asyncCall.Details.Options.CancellationToken; - // if (token.CanBeCanceled) - // { - // token.Register(() => customDisposeAction()); - // return new AsyncUnaryCall(asyncResult, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); - // } - // else - // { - // return new AsyncUnaryCall(asyncResult, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, () => - // { - // customDisposeAction(); - // asyncCall.Cancel(); - // }); - // } - // } - - // public static AsyncServerStreamingCall AsyncServerStreamingCall(CallInvocationDetails call, TRequest req, Action customDisposeAction, string id) - // where TRequest : class - // where TResponse : class - // { - // var asyncCall = new AsyncCall(call); - // asyncCall.StartServerStreamingCall(req); - // var responseStream = new ClientResponseStream(asyncCall); - - // var token = asyncCall.Details.Options.CancellationToken; - // if (token.CanBeCanceled) - // { - // token.Register(() => - // { - // customDisposeAction(); - // }); - - // return new AsyncServerStreamingCall(responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); - // } - // else - // { - // return new AsyncServerStreamingCall(responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, () => { asyncCall.Cancel(); customDisposeAction(); }); - // } - // } - - // public static AsyncClientStreamingCall AsyncClientStreamingCall(CallInvocationDetails call, Action customDisposeAction) - // where TRequest : class - // where TResponse : class - // { - // var asyncCall = new AsyncCall(call); - // var resultTask = asyncCall.ClientStreamingCallAsync(); - // var requestStream = new ClientRequestStream(asyncCall); - - // var token = asyncCall.Details.Options.CancellationToken; - // if (token.CanBeCanceled) - // { - // token.Register(() => customDisposeAction()); - // return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); - // } - // else - // { - // return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, () => { asyncCall.Cancel(); customDisposeAction(); }); - // } - - // } - - // public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(CallInvocationDetails call, Action customDisposeAction) - // where TRequest : class - // where TResponse : class - // { - // var asyncCall = new AsyncCall(call); - // asyncCall.StartDuplexStreamingCall(); - // var requestStream = new ClientRequestStream(asyncCall); - // var responseStream = new ClientResponseStream(asyncCall); - - // var token = asyncCall.Details.Options.CancellationToken; - // if (token.CanBeCanceled) - // { - // token.Register(() => customDisposeAction()); - // return new AsyncDuplexStreamingCall(requestStream, responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); - // } - // else - // { - // return new AsyncDuplexStreamingCall(requestStream, responseStream, asyncCall.ResponseHeadersAsync, asyncCall.GetStatus, asyncCall.GetTrailers, () => { asyncCall.Cancel(); customDisposeAction(); }); - // } - // } - //} -} - -#endif \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs.meta deleted file mode 100644 index b305c4336..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/EditorWindowSupportsCallInvoker.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: f6f9d286ef2588246917d9da07401dee -timeCreated: 1490604045 -licenseType: Pro -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs deleted file mode 100644 index 645aa1120..000000000 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Unity/MagicOnionWindow.cs +++ /dev/null @@ -1,171 +0,0 @@ -//#if UNITY_EDITOR - -//using Grpc.Core; -//using System; -//using System.Collections.Generic; -//using UnityEditor; -//using UnityEngine; - -//namespace MagicOnion -//{ -// public class MagicOnionWindow : EditorWindow -// { -// [MenuItem("Window/MagicOnion")] -// public static void OpenWindow() -// { -// var window = EditorWindow.GetWindow("MagicOnion"); -// window.Show(); -// } - -// static List connectionInfos = new List(); -// static List unregistredSubscriptions = new List(); -// static Dictionary> subscriptions = new Dictionary>(); - -// Vector2 scrollPosition = Vector2.zero; -// long updateCallCount = 0; - -// void Update() -// { -// if (updateCallCount++ % 100 == 0) -// { -// Repaint(); // call OnGUI -// } -// } - -// public static string AddSubscription(Channel channel, string method) -// { -// lock (subscriptions) -// { -// var key = System.Guid.NewGuid().ToString(); - -// var channelRef = new WeakReference(channel); -// subscriptions.Add(key, new KeyValuePair(channelRef, method)); - -// return key; -// } -// } - -// public static void RemoveSubscription(string id) -// { -// lock (subscriptions) -// { -// subscriptions.Remove(id); -// } -// } - -// private void OnGUI() -// { -// var count = GrpcEnvironment.GetCurrentChannels(connectionInfos); - -// scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); -// { -// EditorGUILayout.Space(); -// EditorGUILayout.LabelField("gRPC Channels(" + count + ")", EditorStyles.boldLabel); - -// for (int i = 0; i < count; i++) -// { -// var channel = connectionInfos[i]; - -// using (new EditorGUILayout.HorizontalScope()) -// { -// EditorGUILayout.LabelField(channel.Target); -// if (GUILayout.Button("Shutdown")) -// { -// channel.ShutdownAsync().Subscribe(); -// } -// } - -// EditorGUI.indentLevel += 1; -// { -// using (new EditorGUILayout.HorizontalScope()) -// { -// EditorGUILayout.PrefixLabel("State"); -// EditorGUILayout.LabelField(ChannelStateToString(channel.State)); -// } -// } - -// lock (subscriptions) -// { -// foreach (var item in subscriptions) -// { -// var c = item.Value.Key.Target; -// if (channel == c) -// { -// EditorGUILayout.LabelField(item.Value.Value); -// } -// } -// } - -// EditorGUI.indentLevel -= 1; -// } - -// lock (subscriptions) -// { -// foreach (var item in subscriptions) -// { -// var c = item.Value.Key.Target; -// if (c != null) -// { -// for (int i = 0; i < count; i++) -// { -// var channel = connectionInfos[i]; -// //channel -// if (c == channel) -// { -// goto next; -// } -// } - -// unregistredSubscriptions.Add(item.Value.Value); -// continue; -// } - -// next: -// continue; -// } -// } - -// if (unregistredSubscriptions.Count != 0) -// { -// EditorGUILayout.Space(); -// EditorGUILayout.LabelField("Unfinished Subscriptions", EditorStyles.boldLabel); - -// for (int i = 0; i < unregistredSubscriptions.Count; i++) -// { -// var m = unregistredSubscriptions[i]; -// EditorGUILayout.LabelField(m); -// } - -// unregistredSubscriptions.Clear(); -// } -// } -// EditorGUILayout.EndScrollView(); - -// for (int i = 0; i < connectionInfos.Count; i++) -// { -// connectionInfos[i] = null; // null clear -// } -// } - -// string ChannelStateToString(ChannelState state) -// { -// switch (state) -// { -// case ChannelState.Idle: -// return "Idle"; -// case ChannelState.Connecting: -// return "Connecting"; -// case ChannelState.Ready: -// return "Ready"; -// case ChannelState.TransientFailure: -// return "TransientFailure"; -// case ChannelState.Shutdown: -// return "Shutdown"; -// default: -// return state.ToString(); -// } -// } -// } -//} - -//#endif \ No newline at end of file diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Utils/DynamicAssembly.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Utils/DynamicAssembly.cs index dc83b8e0a..ebc5cfd57 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Utils/DynamicAssembly.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnion/Utils/DynamicAssembly.cs @@ -6,7 +6,12 @@ namespace MagicOnion.Utils { - internal class DynamicAssembly +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + class DynamicAssembly { readonly object gate = new object(); diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnionGenerated.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnionGenerated.cs index c5b019cb2..90d235045 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnionGenerated.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/MagicOnionGenerated.cs @@ -22,8 +22,8 @@ public static void Register() if(isRegistered) return; isRegistered = true; - MagicOnionClientRegistry.Register((x, y) => new Sandbox.NetCoreServer.Hubs.ITestServiceClient(x, y)); - MagicOnionClientRegistry.Register((x, y) => new Sandbox.NetCoreServer.Services.IMyFirstServiceClient(x, y)); + MagicOnionClientRegistry.Register((x, y, z) => new Sandbox.NetCoreServer.Hubs.ITestServiceClient(x, y, z)); + MagicOnionClientRegistry.Register((x, y, z) => new Sandbox.NetCoreServer.Services.IMyFirstServiceClient(x, y, z)); StreamingHubClientRegistry.Register((a, _, b, c, d, e) => new Sandbox.NetCoreServer.Hubs.IGamingHubClient(a, b, c, d, e)); StreamingHubClientRegistry.Register((a, _, b, c, d, e) => new Sandbox.NetCoreServer.Hubs.IBugReproductionHubClient(a, b, c, d, e)); @@ -173,7 +173,8 @@ public int Serialize(ref byte[] bytes, int offset, global::Sandbox.NetCoreServer #pragma warning disable 168 namespace Sandbox.NetCoreServer.Hubs { - using MagicOnion; + using System; + using MagicOnion; using MagicOnion.Client; using Grpc.Core; using MessagePack; @@ -181,18 +182,20 @@ namespace Sandbox.NetCoreServer.Hubs { public class ITestServiceClient : MagicOnionClientBase, global::Sandbox.NetCoreServer.Hubs.ITestService { static readonly Method FooBarBazMethod; + static readonly Func FooBarBazDelegate; static ITestServiceClient() { FooBarBazMethod = new Method(MethodType.Unary, "ITestService", "FooBarBaz", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + FooBarBazDelegate = _FooBarBaz; } ITestServiceClient() { } - public ITestServiceClient(CallInvoker callInvoker, IFormatterResolver resolver) - : base(callInvoker, resolver) + public ITestServiceClient(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters) + : base(callInvoker, resolver, filters) { } @@ -203,6 +206,7 @@ protected override MagicOnionClientBase Clone() clone.option = this.option; clone.callInvoker = this.callInvoker; clone.resolver = this.resolver; + clone.filters = filters; return clone; } @@ -231,11 +235,14 @@ public new ITestService WithOptions(CallOptions option) return base.WithOptions(option); } + static ResponseContext _FooBarBaz(RequestContext __context) + { + return CreateResponseContext, long[]>(__context, FooBarBazMethod); + } + public global::MagicOnion.UnaryResult FooBarBaz(string x, long y) { - var __request = LZ4MessagePackSerializer.Serialize(new DynamicArgumentTuple(x, y), base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(FooBarBazMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync, long[]>("ITestService/FooBarBaz", new DynamicArgumentTuple(x, y), FooBarBazDelegate); } } } @@ -243,8 +250,8 @@ public new ITestService WithOptions(CallOptions option) #pragma warning restore 168 #pragma warning restore 219 #pragma warning restore 414 -#pragma warning restore 618 #pragma warning restore 612 +#pragma warning restore 618 #pragma warning disable 618 #pragma warning disable 612 #pragma warning disable 414 @@ -252,7 +259,8 @@ public new ITestService WithOptions(CallOptions option) #pragma warning disable 168 namespace Sandbox.NetCoreServer.Services { - using MagicOnion; + using System; + using MagicOnion; using MagicOnion.Client; using Grpc.Core; using MessagePack; @@ -260,17 +268,29 @@ namespace Sandbox.NetCoreServer.Services { public class IMyFirstServiceClient : MagicOnionClientBase, global::Sandbox.NetCoreServer.Services.IMyFirstService { static readonly Method ZeroAsyncMethod; + static readonly Func ZeroAsyncDelegate; static readonly Method OneAsyncMethod; + static readonly Func OneAsyncDelegate; static readonly Method SumAsyncMethod; + static readonly Func SumAsyncDelegate; static readonly Method OreOreAsyncMethod; + static readonly Func OreOreAsyncDelegate; static readonly Method OreOre2AsyncMethod; + static readonly Func OreOre2AsyncDelegate; static readonly Method OreOre3AsyncMethod; + static readonly Func OreOre3AsyncDelegate; static readonly Method LegacyZeroAsyncMethod; + static readonly Func LegacyZeroAsyncDelegate; static readonly Method LegacyOneAsyncMethod; + static readonly Func LegacyOneAsyncDelegate; static readonly Method LegacySumAsyncMethod; + static readonly Func LegacySumAsyncDelegate; static readonly Method LegacyOreOreAsyncMethod; + static readonly Func LegacyOreOreAsyncDelegate; static readonly Method LegacyOreOre2AsyncMethod; + static readonly Func LegacyOreOre2AsyncDelegate; static readonly Method LegacyOreOre3AsyncMethod; + static readonly Func LegacyOreOre3AsyncDelegate; static readonly Method ClientStreamingSampleAsyncMethod; static readonly Method ServertSreamingSampleAsyncMethod; static readonly Method DuplexStreamingSampleAyncMethod; @@ -278,17 +298,29 @@ public class IMyFirstServiceClient : MagicOnionClientBase(MethodType.Unary, "IMyFirstService", "ZeroAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + ZeroAsyncDelegate = _ZeroAsync; OneAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "OneAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + OneAsyncDelegate = _OneAsync; SumAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "SumAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + SumAsyncDelegate = _SumAsync; OreOreAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "OreOreAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + OreOreAsyncDelegate = _OreOreAsync; OreOre2AsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "OreOre2Async", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + OreOre2AsyncDelegate = _OreOre2Async; OreOre3AsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "OreOre3Async", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + OreOre3AsyncDelegate = _OreOre3Async; LegacyZeroAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacyZeroAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacyZeroAsyncDelegate = _LegacyZeroAsync; LegacyOneAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacyOneAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacyOneAsyncDelegate = _LegacyOneAsync; LegacySumAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacySumAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacySumAsyncDelegate = _LegacySumAsync; LegacyOreOreAsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacyOreOreAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacyOreOreAsyncDelegate = _LegacyOreOreAsync; LegacyOreOre2AsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacyOreOre2Async", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacyOreOre2AsyncDelegate = _LegacyOreOre2Async; LegacyOreOre3AsyncMethod = new Method(MethodType.Unary, "IMyFirstService", "LegacyOreOre3Async", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + LegacyOreOre3AsyncDelegate = _LegacyOreOre3Async; ClientStreamingSampleAsyncMethod = new Method(MethodType.ClientStreaming, "IMyFirstService", "ClientStreamingSampleAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); ServertSreamingSampleAsyncMethod = new Method(MethodType.ServerStreaming, "IMyFirstService", "ServertSreamingSampleAsync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); DuplexStreamingSampleAyncMethod = new Method(MethodType.DuplexStreaming, "IMyFirstService", "DuplexStreamingSampleAync", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); @@ -298,8 +330,8 @@ static IMyFirstServiceClient() { } - public IMyFirstServiceClient(CallInvoker callInvoker, IFormatterResolver resolver) - : base(callInvoker, resolver) + public IMyFirstServiceClient(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters) + : base(callInvoker, resolver, filters) { } @@ -310,6 +342,7 @@ protected override MagicOnionClientBase Clone() clone.option = this.option; clone.callInvoker = this.callInvoker; clone.resolver = this.resolver; + clone.filters = filters; return clone; } @@ -338,89 +371,128 @@ public new IMyFirstService WithOptions(CallOptions option) return base.WithOptions(option); } + static ResponseContext _ZeroAsync(RequestContext __context) + { + return CreateResponseContext(__context, ZeroAsyncMethod); + } + public global::MagicOnion.UnaryResult ZeroAsync() { - var __request = MagicOnionMarshallers.UnsafeNilBytes; - var __callResult = callInvoker.AsyncUnaryCall(ZeroAsyncMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync("IMyFirstService/ZeroAsync", Nil.Default, ZeroAsyncDelegate); } + static ResponseContext _OneAsync(RequestContext __context) + { + return CreateResponseContext(__context, OneAsyncMethod); + } + public global::MagicOnion.UnaryResult OneAsync(int z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(OneAsyncMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync("IMyFirstService/OneAsync", z, OneAsyncDelegate); + } + static ResponseContext _SumAsync(RequestContext __context) + { + return CreateResponseContext, string>(__context, SumAsyncMethod); } + public global::MagicOnion.UnaryResult SumAsync(int x, int y) { - var __request = LZ4MessagePackSerializer.Serialize(new DynamicArgumentTuple(x, y), base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(SumAsyncMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync, string>("IMyFirstService/SumAsync", new DynamicArgumentTuple(x, y), SumAsyncDelegate); + } + static ResponseContext _OreOreAsync(RequestContext __context) + { + return CreateResponseContext(__context, OreOreAsyncMethod); } + public global::MagicOnion.UnaryResult OreOreAsync(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(OreOreAsyncMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync("IMyFirstService/OreOreAsync", z, OreOreAsyncDelegate); + } + static ResponseContext _OreOre2Async(RequestContext __context) + { + return CreateResponseContext(__context, OreOre2AsyncMethod); } + public global::MagicOnion.UnaryResult OreOre2Async(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(OreOre2AsyncMethod, base.host, base.option, __request); - return new UnaryResult(__callResult, base.resolver); + return InvokeAsync("IMyFirstService/OreOre2Async", z, OreOre2AsyncDelegate); + } + static ResponseContext _OreOre3Async(RequestContext __context) + { + return CreateResponseContext>(__context, OreOre3AsyncMethod); } + public global::MagicOnion.UnaryResult> OreOre3Async(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(OreOre3AsyncMethod, base.host, base.option, __request); - return new UnaryResult>(__callResult, base.resolver); + return InvokeAsync>("IMyFirstService/OreOre3Async", z, OreOre3AsyncDelegate); + } + static ResponseContext _LegacyZeroAsync(RequestContext __context) + { + return CreateResponseContext(__context, LegacyZeroAsyncMethod); } + public global::System.Threading.Tasks.Task> LegacyZeroAsync() { - var __request = MagicOnionMarshallers.UnsafeNilBytes; - var __callResult = callInvoker.AsyncUnaryCall(LegacyZeroAsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult(__callResult, base.resolver)); + return InvokeTaskAsync("IMyFirstService/LegacyZeroAsync", Nil.Default, LegacyZeroAsyncDelegate); + } + static ResponseContext _LegacyOneAsync(RequestContext __context) + { + return CreateResponseContext(__context, LegacyOneAsyncMethod); } + public global::System.Threading.Tasks.Task> LegacyOneAsync(int z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(LegacyOneAsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult(__callResult, base.resolver)); + return InvokeTaskAsync("IMyFirstService/LegacyOneAsync", z, LegacyOneAsyncDelegate); + } + static ResponseContext _LegacySumAsync(RequestContext __context) + { + return CreateResponseContext, string>(__context, LegacySumAsyncMethod); } + public global::System.Threading.Tasks.Task> LegacySumAsync(int x, int y) { - var __request = LZ4MessagePackSerializer.Serialize(new DynamicArgumentTuple(x, y), base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(LegacySumAsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult(__callResult, base.resolver)); + return InvokeTaskAsync, string>("IMyFirstService/LegacySumAsync", new DynamicArgumentTuple(x, y), LegacySumAsyncDelegate); + } + static ResponseContext _LegacyOreOreAsync(RequestContext __context) + { + return CreateResponseContext(__context, LegacyOreOreAsyncMethod); } + public global::System.Threading.Tasks.Task> LegacyOreOreAsync(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(LegacyOreOreAsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult(__callResult, base.resolver)); + return InvokeTaskAsync("IMyFirstService/LegacyOreOreAsync", z, LegacyOreOreAsyncDelegate); + } + static ResponseContext _LegacyOreOre2Async(RequestContext __context) + { + return CreateResponseContext(__context, LegacyOreOre2AsyncMethod); } + public global::System.Threading.Tasks.Task> LegacyOreOre2Async(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(LegacyOreOre2AsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult(__callResult, base.resolver)); + return InvokeTaskAsync("IMyFirstService/LegacyOreOre2Async", z, LegacyOreOre2AsyncDelegate); + } + static ResponseContext _LegacyOreOre3Async(RequestContext __context) + { + return CreateResponseContext>(__context, LegacyOreOre3AsyncMethod); } + public global::System.Threading.Tasks.Task>> LegacyOreOre3Async(global::Sandbox.NetCoreServer.Services.OreOreRequest z) { - var __request = LZ4MessagePackSerializer.Serialize(z, base.resolver); - var __callResult = callInvoker.AsyncUnaryCall(LegacyOreOre3AsyncMethod, base.host, base.option, __request); - return System.Threading.Tasks.Task.FromResult(new UnaryResult>(__callResult, base.resolver)); + return InvokeTaskAsync>("IMyFirstService/LegacyOreOre3Async", z, LegacyOreOre3AsyncDelegate); } + public global::System.Threading.Tasks.Task> ClientStreamingSampleAsync() { var __callResult = callInvoker.AsyncClientStreamingCall(ClientStreamingSampleAsyncMethod, base.host, base.option); return System.Threading.Tasks.Task.FromResult(new ClientStreamingResult(__callResult, base.resolver)); } + public global::System.Threading.Tasks.Task> ServertSreamingSampleAsync(int x, int y, int z) { var __request = LZ4MessagePackSerializer.Serialize(new DynamicArgumentTuple(x, y, z), base.resolver); var __callResult = callInvoker.AsyncServerStreamingCall(ServertSreamingSampleAsyncMethod, base.host, base.option, __request); return System.Threading.Tasks.Task.FromResult(new ServerStreamingResult(__callResult, base.resolver)); } + public global::System.Threading.Tasks.Task> DuplexStreamingSampleAync() { var __callResult = callInvoker.AsyncDuplexStreamingCall(DuplexStreamingSampleAyncMethod, base.host, base.option); @@ -432,8 +504,8 @@ public new IMyFirstService WithOptions(CallOptions option) #pragma warning restore 168 #pragma warning restore 219 #pragma warning restore 414 -#pragma warning restore 618 #pragma warning restore 612 +#pragma warning restore 618 #pragma warning disable 618 #pragma warning disable 612 #pragma warning disable 414 diff --git a/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.pdb.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries.meta similarity index 58% rename from src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.pdb.meta rename to src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries.meta index d3390e967..c023ac56f 100644 --- a/src/MagicOnion.Client.Unity/Assets/Plugins/Grpc.Core/lib/net45/Grpc.Core.pdb.meta +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries.meta @@ -1,7 +1,6 @@ fileFormatVersion: 2 -guid: 49a6ef383d4d43640b6c7fd8168415ae -timeCreated: 1531219385 -licenseType: Free +guid: a2359edd54c3d27468484379a2f9935a +folderAsset: yes DefaultImporter: externalObjects: {} userData: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 000000000..315623926 Binary files /dev/null and b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 000000000..231e9057c --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: ff60b10f8b6e82043b21b49ba63938b4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll new file mode 100644 index 000000000..a99c9077b Binary files /dev/null and b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll differ diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll.meta b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll.meta new file mode 100644 index 000000000..7aa0b924f --- /dev/null +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/NetStandardLibraries/System.Threading.Tasks.Extensions.dll.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 6ab57ed387abdcf4c8e8f4d08e2e0017 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/MagicOnion.Client.Unity/ProjectSettings/ProjectSettings.asset b/src/MagicOnion.Client.Unity/ProjectSettings/ProjectSettings.asset index 1220c022a..4f3b2d419 100644 Binary files a/src/MagicOnion.Client.Unity/ProjectSettings/ProjectSettings.asset and b/src/MagicOnion.Client.Unity/ProjectSettings/ProjectSettings.asset differ diff --git a/src/MagicOnion.UniversalCodeGenerator/CodeAnalysis/Definitions.cs b/src/MagicOnion.UniversalCodeGenerator/CodeAnalysis/Definitions.cs index d8b1e05fb..1cc463b40 100644 --- a/src/MagicOnion.UniversalCodeGenerator/CodeAnalysis/Definitions.cs +++ b/src/MagicOnion.UniversalCodeGenerator/CodeAnalysis/Definitions.cs @@ -119,6 +119,27 @@ public string RequestMarshallerType } } + public string UnaryRequestObject + { + get + { + if (Parameters.Length == 0) + { + return $"Nil.Default"; + } + else if (Parameters.Length == 1) + { + return Parameters[0].ParameterName; + } + else + { + var typeArgs = string.Join(", ", Parameters.Select(x => x.TypeName)); + var parameterNames = string.Join(", ", Parameters.Select(x => x.ParameterName)); + return $"new DynamicArgumentTuple<{typeArgs}>({parameterNames})"; + } + } + } + public string RequestObject() { if (Parameters.Length == 0) diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.cs b/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.cs index 825595d4d..647f8b29f 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.cs +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // このコードはツールによって生成されました。 -// ランタイム バージョン: 15.0.0.0 +// ランタイム バージョン: 16.0.0.0 // // このファイルへの変更は、正しくない動作の原因になる可能性があり、 // コードが再生成されると失われます。 @@ -18,12 +18,9 @@ namespace MagicOnion.Generator /// /// Class to produce the template output /// - - #line 1 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public partial class CodeTemplate : CodeTemplateBase { -#line hidden /// /// Create the template output /// @@ -31,494 +28,207 @@ public virtual string TransformText() { this.Write("#pragma warning disable 618\r\n#pragma warning disable 612\r\n#pragma warning disable" + " 414\r\n#pragma warning disable 219\r\n#pragma warning disable 168\r\n\r\n"); - - #line 13 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace != null ? ("namespace " + Namespace + " {") : "")); - - #line default - #line hidden - this.Write("\r\n using MagicOnion;\r\n using MagicOnion.Client;\r\n using Grpc.Core;\r\n " + - "using MessagePack;\r\n"); - - #line 18 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write("\r\n using System;\r\n\tusing MagicOnion;\r\n using MagicOnion.Client;\r\n using " + + "Grpc.Core;\r\n using MessagePack;\r\n"); foreach(var interfaceDef in Interfaces) { - - #line default - #line hidden this.Write("\r\n"); - - #line 20 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 22 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 23 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" var clientName = interfaceDef.Name + "Client"; - - #line default - #line hidden this.Write(" public class "); - - #line 24 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write(" : MagicOnionClientBase<"); - - #line 24 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write(">, "); - - #line 24 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write("\r\n {\r\n"); - - #line 26 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden - - #line 27 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 29 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write(" static readonly Method "); - - #line 30 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("Method;\r\n"); - - #line 31 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + if(item.MethodType == MethodType.Unary) { + this.Write(" static readonly Func "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Delegate;\r\n"); + } if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 33 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 34 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write("\r\n static "); - - #line 36 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write("()\r\n {\r\n"); - - #line 38 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden - - #line 39 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 41 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write(" "); - - #line 42 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("Method = new Method(MethodType."); - - #line 42 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.MethodType.ToString())); - - #line default - #line hidden this.Write(", \""); - - #line 42 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write("\", \""); - - #line 42 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("\", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshall" + "er);\r\n"); - - #line 43 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + if(item.MethodType == MethodType.Unary) { + this.Write(" "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Delegate = _"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write(";\r\n"); + } if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 45 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 46 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write(" }\r\n\r\n "); - - #line 49 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write("()\r\n {\r\n }\r\n\r\n public "); - - #line 53 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden - this.Write("(CallInvoker callInvoker, IFormatterResolver resolver)\r\n : base(callIn" + - "voker, resolver)\r\n {\r\n }\r\n\r\n protected override MagicOnionC" + - "lientBase<"); - - #line 58 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write("(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters)\r\n" + + " : base(callInvoker, resolver, filters)\r\n {\r\n }\r\n\r\n " + + " protected override MagicOnionClientBase<"); this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write("> Clone()\r\n {\r\n var clone = new "); - - #line 60 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden - this.Write("();\r\n clone.host = this.host;\r\n clone.option = this.option;" + - "\r\n clone.callInvoker = this.callInvoker;\r\n clone.resolver " + - "= this.resolver;\r\n return clone;\r\n }\r\n\r\n public new "); - - #line 68 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write(@"(); + clone.host = this.host; + clone.option = this.option; + clone.callInvoker = this.callInvoker; + clone.resolver = this.resolver; + clone.filters = filters; + return clone; + } + + public new "); this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write(" WithHeaders(Metadata headers)\r\n {\r\n return base.WithHeaders(he" + "aders);\r\n }\r\n\r\n public new "); - - #line 73 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write(" WithCancellationToken(System.Threading.CancellationToken cancellationToken)\r\n " + " {\r\n return base.WithCancellationToken(cancellationToken);\r\n " + " }\r\n\r\n public new "); - - #line 78 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write(" WithDeadline(System.DateTime deadline)\r\n {\r\n return base.WithD" + "eadline(deadline);\r\n }\r\n\r\n public new "); - - #line 83 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write(" WithHost(string host)\r\n {\r\n return base.WithHost(host);\r\n " + " }\r\n\r\n public new "); - - #line 88 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write(" WithOptions(CallOptions option)\r\n {\r\n return base.WithOptions(" + "option);\r\n }\r\n \r\n"); - - #line 93 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden - - #line 94 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 96 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - this.Write(" public "); - - #line 97 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + if(item.MethodType == MethodType.Unary) { + this.Write(" static ResponseContext _"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("(RequestContext __context)\r\n {\r\n"); + if(item.Parameters.Length == 0) { + this.Write(" return CreateResponseContext<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); + this.Write(">(__context, "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Method);\r\n"); + } else { + this.Write(" return CreateResponseContext<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestMarshallerType)); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); + this.Write(">(__context, "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Method);\r\n"); + } + this.Write(" }\r\n"); + } + this.Write("\r\n public "); this.Write(this.ToStringHelper.ToStringWithCulture(item.ToString())); - - #line default - #line hidden this.Write("\r\n {\r\n"); - - #line 99 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.MethodType == MethodType.Unary) { - - #line default - #line hidden - this.Write(" var __request = "); - - #line 100 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" - this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestObject())); - - #line default - #line hidden - this.Write(";\r\n var __callResult = callInvoker.AsyncUnaryCall("); - - #line 101 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" - this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden - this.Write("Method, base.host, base.option, __request);\r\n"); - - #line 102 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.IsResponseTypeTaskOfT) { - - #line default - #line hidden - this.Write(" return System.Threading.Tasks.Task.FromResult(new UnaryResult<"); - - #line 103 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write(" return InvokeTaskAsync<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestMarshallerType)); + this.Write(", "); this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); - - #line default - #line hidden - this.Write(">(__callResult, base.resolver));\r\n"); - - #line 104 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write(">(\""); + this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name + "/" + item.Name)); + this.Write("\", "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.UnaryRequestObject)); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Delegate);\r\n"); } else { - - #line default - #line hidden - this.Write(" return new UnaryResult<"); - - #line 105 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write(" return InvokeAsync<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestMarshallerType)); + this.Write(", "); this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); - - #line default - #line hidden - this.Write(">(__callResult, base.resolver);\r\n"); - - #line 106 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" + this.Write(">(\""); + this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name + "/" + item.Name)); + this.Write("\", "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.UnaryRequestObject)); + this.Write(", "); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); + this.Write("Delegate);\r\n"); } - - #line default - #line hidden - - #line 107 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } else if(item.MethodType ==MethodType.ServerStreaming) { - - #line default - #line hidden this.Write(" var __request = "); - - #line 108 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestObject())); - - #line default - #line hidden this.Write(";\r\n var __callResult = callInvoker.AsyncServerStreamingCall("); - - #line 109 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("Method, base.host, base.option, __request);\r\n return System.Threading." + "Tasks.Task.FromResult(new ServerStreamingResult<"); - - #line 110 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); - - #line default - #line hidden this.Write(">(__callResult, base.resolver));\r\n"); - - #line 111 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } else if(item.MethodType ==MethodType.ClientStreaming) { - - #line default - #line hidden this.Write(" var __callResult = callInvoker.AsyncClientStreamingCall("); - - #line 112 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("Method, base.host, base.option);\r\n return System.Threading.Tasks.Task." + "FromResult(new ClientStreamingResult<"); - - #line 113 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestType)); - - #line default - #line hidden this.Write(", "); - - #line 113 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); - - #line default - #line hidden this.Write(">(__callResult, base.resolver));\r\n"); - - #line 114 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } else if(item.MethodType ==MethodType.DuplexStreaming) { - - #line default - #line hidden this.Write(" var __callResult = callInvoker.AsyncDuplexStreamingCall("); - - #line 115 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("Method, base.host, base.option);\r\n return System.Threading.Tasks.Task." + "FromResult(new DuplexStreamingResult<"); - - #line 116 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.RequestType)); - - #line default - #line hidden this.Write(", "); - - #line 116 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ResponseType)); - - #line default - #line hidden this.Write(">(__callResult, base.resolver));\r\n"); - - #line 117 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write(" }\r\n"); - - #line 119 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 121 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 122 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden this.Write(" }\r\n"); - - #line 124 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#endif \r\n"); - - #line 126 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 127 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" } - - #line default - #line hidden - - #line 128 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\CodeTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace != null ? "}" : "")); - - #line default - #line hidden this.Write("\r\n\r\n#pragma warning restore 168\r\n#pragma warning restore 219\r\n#pragma warning res" + - "tore 414\r\n#pragma warning restore 618\r\n#pragma warning restore 612"); + "tore 414\r\n#pragma warning restore 612\r\n#pragma warning restore 618"); return this.GenerationEnvironment.ToString(); } } - - #line default - #line hidden #region Base class /// /// Base class for this transformation /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public class CodeTemplateBase { #region Fields diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.tt b/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.tt index 73ee47271..6a5daf843 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.tt +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/CodeTemplate.tt @@ -1,4 +1,4 @@ -<#@ template language="C#" #> +<#@ template language="C#" linePragmas="false" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> @@ -11,7 +11,8 @@ #pragma warning disable 168 <#= Namespace != null ? ("namespace " + Namespace + " {") : "" #> - using MagicOnion; + using System; + using MagicOnion; using MagicOnion.Client; using Grpc.Core; using MessagePack; @@ -28,6 +29,9 @@ #if DEBUG <# } #> static readonly Method <#= item.Name #>Method; +<# if(item.MethodType == MethodType.Unary) { #> + static readonly Func <#= item.Name #>Delegate; +<# } #> <# if(item.IsIfDebug) { #> #endif <# } #> @@ -40,6 +44,9 @@ #if DEBUG <# } #> <#= item.Name #>Method = new Method(MethodType.<#= item.MethodType.ToString() #>, "<#= interfaceDef.Name #>", "<#= item.Name #>", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); +<# if(item.MethodType == MethodType.Unary) { #> + <#= item.Name #>Delegate = _<#= item.Name #>; +<# } #> <# if(item.IsIfDebug) { #> #endif <# } #> @@ -50,8 +57,8 @@ { } - public <#= clientName #>(CallInvoker callInvoker, IFormatterResolver resolver) - : base(callInvoker, resolver) + public <#= clientName #>(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters) + : base(callInvoker, resolver, filters) { } @@ -62,6 +69,7 @@ clone.option = this.option; clone.callInvoker = this.callInvoker; clone.resolver = this.resolver; + clone.filters = filters; return clone; } @@ -94,15 +102,24 @@ <# if(item.IsIfDebug) { #> #if DEBUG <# } #> +<# if(item.MethodType == MethodType.Unary) { #> + static ResponseContext _<#= item.Name #>(RequestContext __context) + { +<# if(item.Parameters.Length == 0) { #> + return CreateResponseContext<<#= item.ResponseType #>>(__context, <#= item.Name #>Method); +<# } else { #> + return CreateResponseContext<<#= item.RequestMarshallerType #>, <#= item.ResponseType #>>(__context, <#= item.Name #>Method); +<# } #> + } +<# } #> + public <#= item.ToString() #> { <# if(item.MethodType == MethodType.Unary) { #> - var __request = <#= item.RequestObject() #>; - var __callResult = callInvoker.AsyncUnaryCall(<#= item.Name #>Method, base.host, base.option, __request); <# if(item.IsResponseTypeTaskOfT) { #> - return System.Threading.Tasks.Task.FromResult(new UnaryResult<<#= item.ResponseType #>>(__callResult, base.resolver)); + return InvokeTaskAsync<<#= item.RequestMarshallerType #>, <#= item.ResponseType #>>("<#= interfaceDef.Name + "/" + item.Name #>", <#= item.UnaryRequestObject #>, <#= item.Name #>Delegate); <# } else { #> - return new UnaryResult<<#= item.ResponseType #>>(__callResult, base.resolver); + return InvokeAsync<<#= item.RequestMarshallerType #>, <#= item.ResponseType #>>("<#= interfaceDef.Name + "/" + item.Name #>", <#= item.UnaryRequestObject #>, <#= item.Name #>Delegate); <# } #> <# } else if(item.MethodType ==MethodType.ServerStreaming) { #> var __request = <#= item.RequestObject() #>; @@ -130,5 +147,5 @@ #pragma warning restore 168 #pragma warning restore 219 #pragma warning restore 414 -#pragma warning restore 618 -#pragma warning restore 612 \ No newline at end of file +#pragma warning restore 612 +#pragma warning restore 618 \ No newline at end of file diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.cs b/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.cs index 5f93a28ab..dbbc0598f 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.cs +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // このコードはツールによって生成されました。 -// ランタイム バージョン: 15.0.0.0 +// ランタイム バージョン: 16.0.0.0 // // このファイルへの変更は、正しくない動作の原因になる可能性があり、 // コードが再生成されると失われます。 @@ -17,12 +17,9 @@ namespace MagicOnion.Generator /// /// Class to produce the template output /// - - #line 1 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public partial class EnumTemplate : EnumTemplateBase { -#line hidden /// /// Create the template output /// @@ -30,97 +27,39 @@ public virtual string TransformText() { this.Write("#pragma warning disable 618\r\n#pragma warning disable 612\r\n#pragma warning disable" + " 414\r\n#pragma warning disable 219\r\n#pragma warning disable 168\r\n\r\nnamespace "); - - #line 12 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - - #line default - #line hidden this.Write("\r\n{\r\n using System;\r\n using MessagePack;\r\n\r\n"); - - #line 17 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" foreach(var info in enumSerializationInfos) { - - #line default - #line hidden this.Write(" public sealed class "); - - #line 18 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.Name)); - - #line default - #line hidden this.Write("Formatter : global::MessagePack.Formatters.IMessagePackFormatter<"); - - #line 18 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); - - #line default - #line hidden this.Write(">\r\n {\r\n public int Serialize(ref byte[] bytes, int offset, "); - - #line 20 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); - - #line default - #line hidden this.Write(" value, global::MessagePack.IFormatterResolver formatterResolver)\r\n {\r\n " + " return MessagePackBinary.Write"); - - #line 22 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.UnderlyingType)); - - #line default - #line hidden this.Write("(ref bytes, offset, ("); - - #line 22 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.UnderlyingType)); - - #line default - #line hidden this.Write(")value);\r\n }\r\n \r\n public "); - - #line 25 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); - - #line default - #line hidden this.Write(" Deserialize(byte[] bytes, int offset, global::MessagePack.IFormatterResolver for" + "matterResolver, out int readSize)\r\n {\r\n return ("); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); - - #line default - #line hidden this.Write(")MessagePackBinary.Read"); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(info.UnderlyingType)); - - #line default - #line hidden this.Write("(bytes, offset, out readSize);\r\n }\r\n }\r\n\r\n"); - - #line 31 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\EnumTemplate.tt" } - - #line default - #line hidden this.Write("\r\n}\r\n\r\n#pragma warning restore 168\r\n#pragma warning restore 219\r\n#pragma warning " + "restore 414\r\n#pragma warning restore 612\r\n#pragma warning restore 618"); return this.GenerationEnvironment.ToString(); } } - - #line default - #line hidden #region Base class /// /// Base class for this transformation /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public class EnumTemplateBase { #region Fields diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.tt b/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.tt index e0f956f62..c4afe8e89 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.tt +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/EnumTemplate.tt @@ -1,4 +1,4 @@ -<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ template debug="false" hostspecific="false" language="C#" linePragmas="false" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.cs b/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.cs index d13b59dbe..64b607954 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.cs +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.cs @@ -18,12 +18,9 @@ namespace MagicOnion.Generator /// /// Class to produce the template output /// - - #line 1 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public partial class HubTemplate : HubTemplateBase { -#line hidden /// /// Create the template output /// @@ -31,93 +28,33 @@ public virtual string TransformText() { this.Write("#pragma warning disable 618\r\n#pragma warning disable 612\r\n#pragma warning disable" + " 414\r\n#pragma warning disable 219\r\n#pragma warning disable 168\r\n\r\n"); - - #line 13 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace != null ? ("namespace " + Namespace + " {") : "")); - - #line default - #line hidden this.Write("\r\n using Grpc.Core;\r\n using Grpc.Core.Logging;\r\n using MagicOnion;\r\n " + "using MagicOnion.Client;\r\n using MessagePack;\r\n using System;\r\n using S" + "ystem.Threading.Tasks;\r\n"); - - #line 21 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" foreach(var def in Interfaces) { var interfaceDef= def.hubDef; var receiverDef= def.receiverDef; - - #line default - #line hidden this.Write("\r\n"); - - #line 23 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 25 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } - - #line default - #line hidden - - #line 26 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" var clientName = interfaceDef.Name + "Client"; - - #line default - #line hidden this.Write(" public class "); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write(" : StreamingHubClientBase<"); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write(", "); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(receiverDef.FullName)); - - #line default - #line hidden this.Write(">, "); - - #line 27 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write("\r\n {\r\n static readonly Method method = new Method(MethodType.DuplexStreaming, \""); - - #line 29 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.Name)); - - #line default - #line hidden this.Write("\", \"Connect\", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.Thro" + "ughMarshaller);\r\n\r\n protected override Method DuplexStrea" + "mingAsyncMethod { get { return method; } }\r\n\r\n readonly "); - - #line 33 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write(" __fireAndForgetClient;\r\n\r\n public "); - - #line 35 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write(@"(CallInvoker callInvoker, string host, CallOptions option, IFormatterResolver resolver, ILogger logger) : base(callInvoker, host, option, resolver, logger) { @@ -125,56 +62,21 @@ public virtual string TransformText() } public "); - - #line 41 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write(" FireAndForget()\r\n {\r\n return __fireAndForgetClient;\r\n }" + "\r\n\r\n protected override void OnBroadcastEvent(int methodId, ArraySegment<" + "byte> data)\r\n {\r\n switch (methodId)\r\n {\r\n"); - - #line 50 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" foreach(var item in receiverDef.Methods) { - - #line default - #line hidden this.Write(" case "); - - #line 51 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.HubId)); - - #line default - #line hidden this.Write(": // "); - - #line 51 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("\r\n {\r\n "); - - #line 53 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubOnBroadcastMessage().line1)); - - #line default - #line hidden this.Write("\r\n "); - - #line 54 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubOnBroadcastMessage().line2)); - - #line default - #line hidden this.Write("\r\n }\r\n"); - - #line 56 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end foreach(receiverDef.Methods) - - #line default - #line hidden this.Write(@" default: break; } @@ -185,131 +87,41 @@ protected override void OnResponseEvent(int methodId, object taskCompletionSourc switch (methodId) { "); - - #line 66 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden this.Write(" case "); - - #line 67 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.HubId)); - - #line default - #line hidden this.Write(": // "); - - #line 67 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); - - #line default - #line hidden this.Write("\r\n {\r\n "); - - #line 69 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubOnResponseEvent().line1)); - - #line default - #line hidden this.Write("\r\n "); - - #line 70 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubOnResponseEvent().line2)); - - #line default - #line hidden this.Write("\r\n break;\r\n }\r\n"); - - #line 73 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end foreach(interfaceDef.Methods) - - #line default - #line hidden this.Write(" default:\r\n break;\r\n }\r\n }\r\n " + " \r\n"); - - #line 79 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden - - #line 80 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 82 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end if(IsIfDebug) - - #line default - #line hidden this.Write(" public "); - - #line 83 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToString())); - - #line default - #line hidden this.Write("\r\n {\r\n return "); - - #line 85 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubWriteMessage())); - - #line default - #line hidden this.Write(";\r\n }\r\n\r\n"); - - #line 88 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 90 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end if(IsIfDebug) - - #line default - #line hidden - - #line 91 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end foreach(interfaceDef.Methods) - - #line default - #line hidden this.Write("\r\n class FireAndForgetClient : "); - - #line 93 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write("\r\n {\r\n readonly "); - - #line 95 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write(" __parent;\r\n\r\n public FireAndForgetClient("); - - #line 97 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write(" parentClient)\r\n {\r\n this.__parent = parentClient;\r\n " + " }\r\n\r\n public "); - - #line 102 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.FullName)); - - #line default - #line hidden this.Write(@" FireAndForget() { throw new NotSupportedException(); @@ -326,93 +138,30 @@ public Task WaitForDisconnect() } "); - - #line 117 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" foreach(var item in interfaceDef.Methods) { - - #line default - #line hidden - - #line 118 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 120 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end if(IsIfDebug) - - #line default - #line hidden this.Write(" public "); - - #line 121 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToString())); - - #line default - #line hidden this.Write("\r\n {\r\n return __parent."); - - #line 123 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(item.ToHubFireAndForgetWriteMessage())); - - #line default - #line hidden this.Write(";\r\n }\r\n\r\n"); - - #line 126 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(item.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 128 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end if(IsIfDebug) - - #line default - #line hidden - - #line 129 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end foreach(interfaceDef.Methods) - - #line default - #line hidden this.Write(" }\r\n }\r\n"); - - #line 132 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#endif \r\n"); - - #line 134 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end if(IsIfDebug) - - #line default - #line hidden - - #line 135 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" } // end foreach(Interfaces) - - #line default - #line hidden - - #line 136 "C:\Git\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\HubTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace != null ? "}" : "")); - - #line default - #line hidden this.Write("\r\n\r\n#pragma warning restore 168\r\n#pragma warning restore 219\r\n#pragma warning res" + "tore 414\r\n#pragma warning restore 618\r\n#pragma warning restore 612"); return this.GenerationEnvironment.ToString(); } } - - #line default - #line hidden #region Base class /// /// Base class for this transformation diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.tt b/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.tt index 1398e36ff..c4a693d4a 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.tt +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/HubTemplate.tt @@ -1,4 +1,4 @@ -<#@ template language="C#" #> +<#@ template language="C#" linePragmas="false" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.cs b/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.cs index f8a718be7..2f808b570 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.cs +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // このコードはツールによって生成されました。 -// ランタイム バージョン: 15.0.0.0 +// ランタイム バージョン: 16.0.0.0 // // このファイルへの変更は、正しくない動作の原因になる可能性があり、 // コードが再生成されると失われます。 @@ -18,12 +18,9 @@ namespace MagicOnion.Generator /// /// Class to produce the template output /// - - #line 1 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public partial class RegisterTemplate : RegisterTemplateBase { -#line hidden /// /// Create the template output /// @@ -31,12 +28,7 @@ public virtual string TransformText() { this.Write("#pragma warning disable 618\r\n#pragma warning disable 612\r\n#pragma warning disable" + " 414\r\n#pragma warning disable 219\r\n#pragma warning disable 168\r\n\r\nnamespace "); - - #line 13 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - - #line default - #line hidden this.Write(@" { using global::System; @@ -50,150 +42,52 @@ public static partial class MagicOnionInitializer static bool isRegistered = false; "); - - #line 25 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" if( !UnuseUnityAttribute) { - - #line default - #line hidden this.Write(" [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeL" + "oadType.BeforeSceneLoad)]\r\n"); - - #line 27 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } - - #line default - #line hidden this.Write(" public static void Register()\r\n {\r\n if(isRegistered) re" + "turn;\r\n isRegistered = true;\r\n\r\n"); - - #line 33 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" foreach(var interfaceDef in Interfaces) { var clientName = (interfaceDef.Namespace != null ? interfaceDef.Namespace + "." : "") + interfaceDef.Name + "Client"; - - #line default - #line hidden - - #line 34 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 36 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } - - #line default - #line hidden this.Write(" MagicOnionClientRegistry<"); - - #line 37 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.ToString())); - - #line default - #line hidden - this.Write(">.Register((x, y) => new "); - - #line 37 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" + this.Write(">.Register((x, y, z) => new "); this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden - this.Write("(x, y));\r\n"); - - #line 38 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" + this.Write("(x, y, z));\r\n"); if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 40 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } - - #line default - #line hidden - - #line 41 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } // foreach - - #line default - #line hidden this.Write("\r\n"); - - #line 43 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" foreach(var (interfaceDef, receiverDef) in HubInterfaces) { var clientName = (interfaceDef.Namespace != null ? interfaceDef.Namespace + "." : "") + interfaceDef.Name + "Client"; - - #line default - #line hidden - - #line 44 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#if DEBUG\r\n"); - - #line 46 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } - - #line default - #line hidden this.Write(" StreamingHubClientRegistry<"); - - #line 47 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(interfaceDef.ToString())); - - #line default - #line hidden this.Write(", "); - - #line 47 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(receiverDef.ToString())); - - #line default - #line hidden this.Write(">.Register((a, _, b, c, d, e) => new "); - - #line 47 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(clientName)); - - #line default - #line hidden this.Write("(a, b, c, d, e));\r\n"); - - #line 48 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" if(interfaceDef.IsIfDebug) { - - #line default - #line hidden this.Write("#endif\r\n"); - - #line 50 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } - - #line default - #line hidden - - #line 51 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\RegisterTemplate.tt" } // foreach - - #line default - #line hidden this.Write(" }\r\n }\r\n}\r\n\r\n#pragma warning restore 168\r\n#pragma warning restore 219\r\n" + "#pragma warning restore 414\r\n#pragma warning restore 612\r\n#pragma warning restor" + "e 618"); return this.GenerationEnvironment.ToString(); } } - - #line default - #line hidden #region Base class /// /// Base class for this transformation /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public class RegisterTemplateBase { #region Fields diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.tt b/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.tt index 881c84ac3..e3830ea83 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.tt +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/RegisterTemplate.tt @@ -1,4 +1,4 @@ -<#@ template language="C#" #> +<#@ template language="C#" linePragmas="false" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> @@ -34,7 +34,7 @@ namespace <#= Namespace #> <# if(interfaceDef.IsIfDebug) { #> #if DEBUG <# } #> - MagicOnionClientRegistry<<#= interfaceDef.ToString() #>>.Register((x, y) => new <#= clientName #>(x, y)); + MagicOnionClientRegistry<<#= interfaceDef.ToString() #>>.Register((x, y, z) => new <#= clientName #>(x, y, z)); <# if(interfaceDef.IsIfDebug) { #> #endif <# } #> diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.cs b/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.cs index 7883cd248..8f733cbda 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.cs +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // このコードはツールによって生成されました。 -// ランタイム バージョン: 15.0.0.0 +// ランタイム バージョン: 16.0.0.0 // // このファイルへの変更は、正しくない動作の原因になる可能性があり、 // コードが再生成されると失われます。 @@ -17,12 +17,9 @@ namespace MagicOnion.Generator /// /// Class to produce the template output /// - - #line 1 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public partial class ResolverTemplate : ResolverTemplateBase { -#line hidden /// /// Create the template output /// @@ -30,34 +27,14 @@ public virtual string TransformText() { this.Write("#pragma warning disable 618\r\n#pragma warning disable 612\r\n#pragma warning disable" + " 414\r\n#pragma warning disable 219\r\n#pragma warning disable 168\r\n\r\nnamespace "); - - #line 12 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(Namespace)); - - #line default - #line hidden this.Write("\r\n{\r\n using System;\r\n using MessagePack;\r\n\r\n public class "); - - #line 17 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write(" : global::MessagePack.IFormatterResolver\r\n {\r\n public static readonly " + "global::MessagePack.IFormatterResolver Instance = new "); - - #line 19 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write("();\r\n\r\n "); - - #line 21 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write(@"() { @@ -75,12 +52,7 @@ static class FormatterCache static FormatterCache() { var f = "); - - #line 37 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write(@"GetFormatterHelper.GetFormatter(typeof(T)); if (f != null) { @@ -91,56 +63,21 @@ static FormatterCache() } internal static class "); - - #line 46 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write("GetFormatterHelper\r\n {\r\n static readonly global::System.Collections.Gen" + "eric.Dictionary lookup;\r\n\r\n static "); - - #line 50 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(ResolverName)); - - #line default - #line hidden this.Write("GetFormatterHelper()\r\n {\r\n lookup = new global::System.Collecti" + "ons.Generic.Dictionary("); - - #line 52 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(registerInfos.Length)); - - #line default - #line hidden this.Write(")\r\n {\r\n"); - - #line 54 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" for(var i = 0; i < registerInfos.Length; i++) { var x = registerInfos[i]; - - #line default - #line hidden this.Write(" {typeof("); - - #line 55 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(x.FullName)); - - #line default - #line hidden this.Write("), "); - - #line 55 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(i)); - - #line default - #line hidden this.Write(" },\r\n"); - - #line 56 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" } - - #line default - #line hidden this.Write(@" }; } @@ -155,47 +92,24 @@ internal static object GetFormatter(Type t) switch (key) { "); - - #line 70 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" for(var i = 0; i < registerInfos.Length; i++) { var x = registerInfos[i]; - - #line default - #line hidden this.Write(" case "); - - #line 71 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(i)); - - #line default - #line hidden this.Write(": return new "); - - #line 71 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" this.Write(this.ToStringHelper.ToStringWithCulture(x.FormatterName.StartsWith("global::") ? x.FormatterName: FormatterNamespace + "." + x.FormatterName)); - - #line default - #line hidden this.Write(";\r\n"); - - #line 72 "C:\GitHubRepositories\MagicOnion\src\MagicOnion.UniversalCodeGenerator\Generator\ResolverTemplate.tt" } - - #line default - #line hidden this.Write(" default: return null;\r\n }\r\n }\r\n }\r\n}\r\n\r\n#pra" + "gma warning restore 168\r\n#pragma warning restore 219\r\n#pragma warning restore 41" + "4\r\n#pragma warning restore 612\r\n#pragma warning restore 618"); return this.GenerationEnvironment.ToString(); } } - - #line default - #line hidden #region Base class /// /// Base class for this transformation /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] public class ResolverTemplateBase { #region Fields diff --git a/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.tt b/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.tt index 3442198b1..993233f35 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.tt +++ b/src/MagicOnion.UniversalCodeGenerator/Generator/ResolverTemplate.tt @@ -1,4 +1,4 @@ -<#@ template language="C#" #> +<#@ template language="C#" linePragmas="false" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> diff --git a/src/MagicOnion.UniversalCodeGenerator/Properties/launchSettings.json b/src/MagicOnion.UniversalCodeGenerator/Properties/launchSettings.json index 51f92b939..64805aabc 100644 --- a/src/MagicOnion.UniversalCodeGenerator/Properties/launchSettings.json +++ b/src/MagicOnion.UniversalCodeGenerator/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "MagicOnion.UniversalCodeGenerator": { "commandName": "Project", - "commandLineArgs": "-i \"C:\\Git\\MagicOnion\\sandbox\\Sandbox.NetCoreServer\\Sandbox.NetCoreServer.csproj\" -o \"C:\\Git\\MagicOnion\\src\\MagicOnion.Client.Unity\\Assets\\Scripts\\MagicOnionGenerated.cs\"" + "commandLineArgs": "-i \"C:\\GitHubRepositories\\MagicOnion\\sandbox\\Sandbox.NetCoreServer\\Sandbox.NetCoreServer.csproj\" -o \"C:\\GitHubRepositories\\MagicOnion\\src\\MagicOnion.Client.Unity\\Assets\\Scripts\\MagicOnionGenerated.cs\"" } } } \ No newline at end of file diff --git a/src/MagicOnion/Client/ClientFilter.cs b/src/MagicOnion/Client/ClientFilter.cs new file mode 100644 index 000000000..936a0d000 --- /dev/null +++ b/src/MagicOnion/Client/ClientFilter.cs @@ -0,0 +1,182 @@ +using Grpc.Core; +using MessagePack; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace MagicOnion.Client +{ + public interface IClientFilter + { + ValueTask SendAsync(RequestContext context, Func> next); + } + + public abstract class RequestContext + { + public string MethodPath { get; } + public CallOptions CallOptions { get; } + public Type ResponseType { get; } + public abstract Type RequestType { get; } + + Dictionary items; + public IDictionary Items + { + get + { + if (items == null) + { + items = new Dictionary(); + } + return items; + } + } + + // internal use to avoid lambda capture. + internal MagicOnionClientBase Client { get; } + internal IClientFilter[] Filters { get; } + internal Func RequestMethod { get; } + + internal RequestContext(MagicOnionClientBase client, string methodPath, CallOptions callOptions, Type responseType, IClientFilter[] filters, Func requestMethod) + { + this.Client = client; + this.MethodPath = methodPath; + this.CallOptions = callOptions; + this.ResponseType = responseType; + this.Filters = filters; + this.RequestMethod = requestMethod; + } + } + + public class RequestContext : RequestContext + { + public T Request { get; } + public override Type RequestType => typeof(T); + + public RequestContext(T request, MagicOnionClientBase client, string methodPath, CallOptions callOptions, Type responseType, IClientFilter[] filters, Func requestMethod) + : base(client, methodPath, callOptions, responseType, filters, requestMethod) + { + this.Request = request; + } + } + + public abstract class ResponseContext : IDisposable + { + public abstract Task ResponseHeadersAsync { get; } + public abstract Status GetStatus(); + public abstract Metadata GetTrailers(); + public abstract void Dispose(); + public abstract Type ResponseType { get; } + + public abstract Task WaitResponseAsync(); + + public ResponseContext As() + { + return this as ResponseContext; + } + + public Task GetResponseAs() + { + var t = this as ResponseContext; + if (t == null) return Task.FromResult(default(T)); + + return t.ResponseAsync; + } + } + + public sealed class ResponseContext : ResponseContext + { + readonly AsyncUnaryCall inner; + readonly IFormatterResolver resolver; + readonly bool isMock; + bool deserialized; + + T responseObject; // cache value. + + // mock + readonly Metadata trailers; + readonly Metadata responseHeaders; + readonly Status status; + + public ResponseContext(AsyncUnaryCall inner, IFormatterResolver resolver) + { + this.isMock = false; + this.inner = inner; + this.resolver = resolver; + } + + public ResponseContext(T responseObject) + : this(responseObject, new Metadata(), new Metadata(), Status.DefaultSuccess) + { + } + + public ResponseContext(T responseObject, Metadata trailers, Metadata responseHeaders, Status status) + { + this.isMock = true; + this.responseObject = responseObject; + this.trailers = trailers; + this.responseHeaders = responseHeaders; + this.status = status; + } + + async Task Deserialize() + { + if (deserialized) + { + return responseObject; + } + else + { + var bytes = await inner.ResponseAsync.ConfigureAwait(false); + responseObject = LZ4MessagePackSerializer.Deserialize(bytes, resolver); + deserialized = true; + return responseObject; + } + } + + public Task ResponseAsync => !isMock ? Deserialize() : Task.FromResult(responseObject); + + public override async Task WaitResponseAsync() + { + await ResponseAsync; + return this; + } + + public override Type ResponseType => typeof(T); + + public override Task ResponseHeadersAsync => !isMock ? inner.ResponseHeadersAsync : Task.FromResult(responseHeaders); + + public override void Dispose() + { + if (!isMock) + { + inner.Dispose(); + } + } + + public override Status GetStatus() + { + return !isMock ? inner.GetStatus() : status; + } + + public override Metadata GetTrailers() + { + return !isMock ? inner.GetTrailers() : trailers; + } + + public ResponseContext WithNewResult(T result) + { + if (isMock) + { + return new ResponseContext(result, trailers, responseHeaders, status); + } + else + { + var newContext = new ResponseContext(inner, resolver); + newContext.deserialized = true; + newContext.responseObject = result; + return newContext; + } + } + } +} diff --git a/src/MagicOnion/Client/DynamicClientBuilder.cs b/src/MagicOnion/Client/DynamicClientBuilder.cs index 21e13a56d..4b26eb2b1 100644 --- a/src/MagicOnion/Client/DynamicClientBuilder.cs +++ b/src/MagicOnion/Client/DynamicClientBuilder.cs @@ -12,7 +12,12 @@ namespace MagicOnion.Client { - internal static class DynamicClientAssemblyHolder +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + static class DynamicClientAssemblyHolder { public const string ModuleName = "MagicOnion.Client.DynamicClient"; @@ -34,7 +39,13 @@ public static AssemblyBuilder Save() #endif } - internal static class DynamicClientBuilder +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + static class DynamicClientBuilder + where T : IService { public static readonly Type ClientType; static readonly Type bytesMethod = typeof(Method<,>).MakeGenericType(new[] { typeof(byte[]), typeof(byte[]) }); @@ -130,11 +141,49 @@ static void DefineStaticConstructor(TypeBuilder typeBuilder, Type interfaceType, il.Emit(OpCodes.Ldsfld, throughMarshaller); il.Emit(OpCodes.Newobj, bytesMethod.GetConstructors()[0]); il.Emit(OpCodes.Stsfld, def.FieldMethod); + + if (def.MethodType == MethodType.Unary) + { + DefineUnaryRequestDelegate(il, typeBuilder, interfaceType, def); + } } il.Emit(OpCodes.Ret); } + static void DefineUnaryRequestDelegate(ILGenerator staticContructorGenerator, TypeBuilder typeBuilder, Type interfaceType, MethodDefinition definition) + { + // static ResponseContext _Method(RequestContext context); + MethodBuilder method; + { + method = typeBuilder.DefineMethod("_" + definition.MethodInfo.Name, MethodAttributes.Private | MethodAttributes.Static, + typeof(ResponseContext), + new[] { typeof(RequestContext) }); + var il = method.GetILGenerator(); + + // CreateResponseContext(Context, Method); + var createMethod = typeof(MagicOnionClientBase) + .GetMethod("CreateResponseContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) + .MakeGenericMethod(definition.RequestType, definition.ResponseType); + + il.Emit(OpCodes.Ldarg_0); // context + il.Emit(OpCodes.Ldsfld, definition.FieldMethod); // method + il.Emit(OpCodes.Call, createMethod); + il.Emit(OpCodes.Ret); + } + + // static readonly Func methodDelegate = _Method; + { + definition.UnaryRequestDelegate = typeBuilder.DefineField(definition.MethodInfo.Name + "Delegate", typeof(Func), FieldAttributes.Private | FieldAttributes.Static); + + var il = staticContructorGenerator; + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Ldftn, method); + il.Emit(OpCodes.Newobj, typeof(Func).GetConstructors()[0]); + il.Emit(OpCodes.Stsfld, definition.UnaryRequestDelegate); + } + } + static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinition[] definitions) { ConstructorInfo emptyCtor; @@ -150,15 +199,16 @@ static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinit emptyCtor = ctor; } - // .ctor(CallInvoker, IFormatterResolver):base(callInvoker, resolver) + // .ctor(CallInvoker, IFormatterResolver, IClientFilter[]):base(callInvoker, resolver, clientFilters) { - var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(CallInvoker), typeof(IFormatterResolver) }); + var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(CallInvoker), typeof(IFormatterResolver), typeof(IClientFilter[]) }); var il = ctor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(CallInvoker), typeof(IFormatterResolver) }, null)); + il.Emit(OpCodes.Ldarg_3); + il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(CallInvoker), typeof(IFormatterResolver), typeof(IClientFilter[]) }, null)); il.Emit(OpCodes.Ret); } @@ -167,11 +217,13 @@ static ConstructorInfo DefineConstructors(TypeBuilder typeBuilder, MethodDefinit static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDefinition[] definitions, ConstructorInfo emptyCtor) { + var filedHolderType = typeof(MagicOnionClientBase); var baseType = typeof(MagicOnionClientBase<>).MakeGenericType(interfaceType); - var hostField = baseType.GetField("host", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var optionField = baseType.GetField("option", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var invokerField = baseType.GetField("callInvoker", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - var resolverField = baseType.GetField("resolver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var hostField = filedHolderType.GetField("host", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var optionField = filedHolderType.GetField("option", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var invokerField = filedHolderType.GetField("callInvoker", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var resolverField = filedHolderType.GetField("resolver", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var filtersField = filedHolderType.GetField("filters", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // Clone { @@ -202,6 +254,11 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDef il.Emit(OpCodes.Ldfld, resolverField); il.Emit(OpCodes.Stfld, resolverField); + il.Emit(OpCodes.Dup); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, filtersField); + il.Emit(OpCodes.Stfld, filtersField); + il.Emit(OpCodes.Ret); } // Overrides @@ -281,81 +338,101 @@ static void DefineMethods(TypeBuilder typeBuilder, Type interfaceType, MethodDef switch (def.MethodType) { case MethodType.Unary: - case MethodType.ServerStreaming: - il.DeclareLocal(typeof(byte[])); // request - if (def.MethodType == MethodType.Unary) { - il.DeclareLocal(typeof(AsyncUnaryCall)); // callResult + // base.InvokeAsync/InvokeTaskAsync(string path, TRequest request, Func requestMethod) + + // this. + il.Emit(OpCodes.Ldarg_0); + + // path + il.Emit(OpCodes.Ldstr, def.Path); + + // create request + for (int j = 0; j < parameters.Length; j++) + { + il.Emit(OpCodes.Ldarg, j + 1); + } + if (parameters.Length == 0) + { + // use empty byte[0] + il.Emit(OpCodes.Ldsfld, nilBytes); + } + else if (parameters.Length == 1) + { + // already loaded parameter. + } + else + { + // call new DynamicArgumentTuple + il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + } + + // requestMethod + il.Emit(OpCodes.Ldsfld, def.UnaryRequestDelegate); + + // InvokeAsync/InvokeTaskAsync + var invokeMethod = def.ResponseIsTask + ? baseType.GetMethod("InvokeTaskAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance) + : baseType.GetMethod("InvokeAsync", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance); + invokeMethod = invokeMethod.MakeGenericMethod(def.RequestType, def.ResponseType); + il.Emit(OpCodes.Callvirt, invokeMethod); } - else + + break; + case MethodType.ServerStreaming: { + il.DeclareLocal(typeof(byte[])); // request il.DeclareLocal(typeof(AsyncServerStreamingCall)); - } - // create request - for (int j = 0; j < parameters.Length; j++) - { - il.Emit(OpCodes.Ldarg, j + 1); - } - if (parameters.Length == 0) - { - // use empty byte[0] - il.Emit(OpCodes.Ldsfld, nilBytes); - } - else if (parameters.Length == 1) - { - // already loaded parameter. + // create request + for (int j = 0; j < parameters.Length; j++) + { + il.Emit(OpCodes.Ldarg, j + 1); + } + if (parameters.Length == 0) + { + // use empty byte[0] + il.Emit(OpCodes.Ldsfld, nilBytes); + } + else if (parameters.Length == 1) + { + // already loaded parameter. + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); + } + else + { + // call new DynamicArgumentTuple + il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); + } + il.Emit(OpCodes.Stloc_0); + + // create ***Result il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); - } - else - { - // call new DynamicArgumentTuple - il.Emit(OpCodes.Newobj, def.RequestType.GetConstructors()[0]); + il.Emit(OpCodes.Ldfld, invokerField); + il.Emit(OpCodes.Ldsfld, def.FieldMethod); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - il.Emit(OpCodes.Call, callMessagePackSerialize.MakeGenericMethod(def.RequestType)); - } - il.Emit(OpCodes.Stloc_0); - - // create ***Result - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, invokerField); - il.Emit(OpCodes.Ldsfld, def.FieldMethod); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, hostField); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, optionField); - il.Emit(OpCodes.Ldloc_0); - if (def.MethodType == MethodType.Unary) - { - il.Emit(OpCodes.Callvirt, typeof(CallInvoker).GetMethod("AsyncUnaryCall").MakeGenericMethod(typeof(byte[]), typeof(byte[]))); - } - else - { + il.Emit(OpCodes.Ldfld, hostField); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, optionField); + il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, typeof(CallInvoker).GetMethod("AsyncServerStreamingCall").MakeGenericMethod(typeof(byte[]), typeof(byte[]))); - } - il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Stloc_1); - // create return result - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldfld, resolverField); - Type resultType; - if (def.MethodType == MethodType.Unary) - { - resultType = typeof(UnaryResult<>).MakeGenericType(def.ResponseType); - il.Emit(OpCodes.Newobj, resultType.GetConstructors().OrderBy(x => x.GetParameters().Length).Last()); - } - else - { - resultType = typeof(ServerStreamingResult<>).MakeGenericType(def.ResponseType); + // create return result + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, resolverField); + var resultType = typeof(ServerStreamingResult<>).MakeGenericType(def.ResponseType); il.Emit(OpCodes.Newobj, resultType.GetConstructors()[0]); - } - if (def.ResponseIsTask) - { - il.Emit(OpCodes.Call, typeof(Task).GetMethod("FromResult").MakeGenericMethod(resultType)); + if (def.ResponseIsTask) + { + il.Emit(OpCodes.Call, typeof(Task).GetMethod("FromResult").MakeGenericMethod(resultType)); + } } break; case MethodType.ClientStreaming: @@ -481,6 +558,9 @@ class MethodDefinition public FieldInfo FieldMethod; public Type RequestType; public Type ResponseType; + + // unary only, set after define static fields + public FieldInfo UnaryRequestDelegate; } } } diff --git a/src/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs b/src/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs index cdb829706..1b55c9653 100644 --- a/src/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs +++ b/src/MagicOnion/Client/EmbeddedServices/HeartbeatClient.cs @@ -1,6 +1,7 @@ using Grpc.Core; using MagicOnion.Server.EmbeddedServices; using MessagePack; +using System; using System.Threading.Tasks; #if !NON_UNITY @@ -38,7 +39,7 @@ public HeartbeatClient(Channel channel) } public HeartbeatClient(CallInvoker callInvoker) - : base(callInvoker, null) + : base(callInvoker, null, Array.Empty()) { } diff --git a/src/MagicOnion/Client/EmbeddedServices/PingClient.cs b/src/MagicOnion/Client/EmbeddedServices/PingClient.cs index d2249d4bb..d2440a7f1 100644 --- a/src/MagicOnion/Client/EmbeddedServices/PingClient.cs +++ b/src/MagicOnion/Client/EmbeddedServices/PingClient.cs @@ -1,6 +1,7 @@ using Grpc.Core; using MagicOnion.Server.EmbeddedServices; using MessagePack; +using System; #if !NON_UNITY @@ -37,7 +38,7 @@ public PingClient(Channel channel) } public PingClient(CallInvoker callInvoker) - : base(callInvoker, null) + : base(callInvoker, null, Array.Empty()) { } @@ -53,8 +54,13 @@ protected override MagicOnionClientBase Clone() public UnaryResult Ping() { - var __callResult = callInvoker.AsyncUnaryCall(Method, base.host, base.option, MagicOnionMarshallers.UnsafeNilBytes); - return new UnaryResult(__callResult, MessagePack.Resolvers.BuiltinResolver.Instance); + return InvokeAsync("IMagicOnionEmbeddedPing/Ping", MagicOnionMarshallers.UnsafeNilBytes, __ctx => + { + var __self = (PingClient)__ctx.Client; + var __request = MagicOnionMarshallers.UnsafeNilBytes; + var __callResult = __self.callInvoker.AsyncUnaryCall(PingClient.Method, __self.host, __ctx.CallOptions, __request); + return new ResponseContext(__callResult, MessagePack.Resolvers.BuiltinResolver.Instance); + }); } } } \ No newline at end of file diff --git a/src/MagicOnion/Client/InterceptInvokeHelper.cs b/src/MagicOnion/Client/InterceptInvokeHelper.cs new file mode 100644 index 000000000..870155e54 --- /dev/null +++ b/src/MagicOnion/Client/InterceptInvokeHelper.cs @@ -0,0 +1,158 @@ +using System.Threading.Tasks; + +namespace MagicOnion.Client +{ + internal static class InterceptInvokeHelper + { + public static ValueTask InvokeWithFilter(RequestContext context) + { + switch (context.Filters.Length) + { + case 0: + return new ValueTask(context.RequestMethod(context)); + case 1: + return InvokeWithFilter1(context); + case 2: + return InvokeWithFilter2(context); + case 3: + return InvokeWithFilter3(context); + case 4: + return InvokeWithFilter4(context); + case 5: + return InvokeWithFilter5(context); + case 6: + return InvokeWithFilter6(context); + case 7: + return InvokeWithFilter7(context); + case 8: + return InvokeWithFilter8(context); + case 9: + return InvokeWithFilter9(context); + case 10: + return InvokeWithFilter10(context); + default: + return InvokeRecursive(-1, context); + } + } + + static ValueTask InvokeWithFilter1(RequestContext context) + { + return context.Filters[0].SendAsync(context, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())); + } + + static ValueTask InvokeWithFilter2(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))); + } + + static ValueTask InvokeWithFilter3(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))); + } + + static ValueTask InvokeWithFilter4(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))); + } + + static ValueTask InvokeWithFilter5(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))); + } + + static ValueTask InvokeWithFilter6(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))); + } + + static ValueTask InvokeWithFilter7(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))))); + } + + static ValueTask InvokeWithFilter8(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))))); + } + + static ValueTask InvokeWithFilter9(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + x8 => x8.Filters[8].SendAsync(x8, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync())))))))))); + } + + static ValueTask InvokeWithFilter10(RequestContext context) + { + return context.Filters[0].SendAsync(context, + x1 => x1.Filters[1].SendAsync(x1, + x2 => x2.Filters[2].SendAsync(x2, + x3 => x3.Filters[3].SendAsync(x3, + x4 => x4.Filters[4].SendAsync(x4, + x5 => x5.Filters[5].SendAsync(x5, + x6 => x6.Filters[6].SendAsync(x6, + x7 => x7.Filters[7].SendAsync(x7, + x8 => x8.Filters[8].SendAsync(x8, + x9 => x9.Filters[9].SendAsync(x9, + ctx => new ValueTask(ctx.RequestMethod(ctx).WaitResponseAsync()))))))))))); + } + + // for invoke N filters(slow path). + + static ValueTask InvokeRecursive(int index, RequestContext context) + { + index += 1; // start from -1 + if (index != context.Filters.Length) + { + return context.Filters[index].SendAsync(context, ctx => InvokeRecursive(index, ctx)); + } + else + { + return new ValueTask(context.RequestMethod(context).WaitResponseAsync()); + } + } + } +} \ No newline at end of file diff --git a/src/MagicOnion/Client/MagicOnionClient.cs b/src/MagicOnion/Client/MagicOnionClient.cs index 7d5356bcb..b65b28f8b 100644 --- a/src/MagicOnion/Client/MagicOnionClient.cs +++ b/src/MagicOnion/Client/MagicOnionClient.cs @@ -6,26 +6,46 @@ namespace MagicOnion.Client { public static class MagicOnionClient { + static readonly IClientFilter[] emptyFilters = Array.Empty(); + public static T Create(Channel channel) where T : IService { - return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver); + return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver, emptyFilters); } public static T Create(CallInvoker invoker) where T : IService { - return Create(invoker, MessagePackSerializer.DefaultResolver); + return Create(invoker, MessagePackSerializer.DefaultResolver, emptyFilters); + } + + public static T Create(Channel channel, IClientFilter[] clientFilters) + where T : IService + { + return Create(new DefaultCallInvoker(channel), MessagePackSerializer.DefaultResolver, clientFilters); + } + + public static T Create(CallInvoker invoker, IClientFilter[] clientFilters) + where T : IService + { + return Create(invoker, MessagePackSerializer.DefaultResolver, clientFilters); } public static T Create(Channel channel, IFormatterResolver resolver) where T : IService { - return Create(new DefaultCallInvoker(channel), resolver); + return Create(new DefaultCallInvoker(channel), resolver, emptyFilters); } public static T Create(CallInvoker invoker, IFormatterResolver resolver) where T : IService + { + return Create(invoker, resolver, emptyFilters); + } + + public static T Create(CallInvoker invoker, IFormatterResolver resolver, IClientFilter[] clientFilters) + where T : IService { if (invoker == null) throw new ArgumentNullException(nameof(invoker)); @@ -36,12 +56,12 @@ public static T Create(CallInvoker invoker, IFormatterResolver resolver) throw new InvalidOperationException("Does not registered client factory, dynamic code generation is not supported on IL2CPP. Please use code generator(moc)."); #else var t = DynamicClientBuilder.ClientType; - return (T)Activator.CreateInstance(t, invoker, resolver); + return (T)Activator.CreateInstance(t, invoker, resolver, clientFilters); #endif } else { - return ctor(invoker, resolver); + return ctor(invoker, resolver, clientFilters); } } } @@ -49,9 +69,9 @@ public static T Create(CallInvoker invoker, IFormatterResolver resolver) public static class MagicOnionClientRegistry where T : IService { - public static Func consturtor; + public static Func consturtor; - public static void Register(Func ctor) + public static void Register(Func ctor) { consturtor = ctor; } diff --git a/src/MagicOnion/Client/MagicOnionClientBase.cs b/src/MagicOnion/Client/MagicOnionClientBase.cs index edfbccf21..8c102fd99 100644 --- a/src/MagicOnion/Client/MagicOnionClientBase.cs +++ b/src/MagicOnion/Client/MagicOnionClientBase.cs @@ -1,27 +1,88 @@ using Grpc.Core; -using MagicOnion.Server; using MessagePack; using System; using System.Threading; +using System.Threading.Tasks; namespace MagicOnion.Client { - public abstract class MagicOnionClientBase where T : IService + public abstract class MagicOnionClientBase { protected string host; protected CallOptions option; protected CallInvoker callInvoker; protected IFormatterResolver resolver; + protected IClientFilter[] filters; - protected MagicOnionClientBase() + static protected ResponseContext CreateResponseContext(RequestContext context, Method method) { + var self = context.Client; + var message = LZ4MessagePackSerializer.Serialize(((RequestContext)context).Request, self.resolver); + var callResult = self.callInvoker.AsyncUnaryCall(method, self.host, context.CallOptions, message); + return new ResponseContext(callResult, self.resolver); + } + } + public abstract class MagicOnionClientBase : MagicOnionClientBase + where T : IService + { + protected MagicOnionClientBase() + { } - protected MagicOnionClientBase(CallInvoker callInvoker, IFormatterResolver resolver) + protected MagicOnionClientBase(CallInvoker callInvoker, IFormatterResolver resolver, IClientFilter[] filters) { this.callInvoker = callInvoker; this.resolver = resolver; + this.filters = filters; + } + + protected UnaryResult InvokeAsync(string path, TRequest request, Func requestMethod) + { + var future = InvokeAsyncCore(path, request, requestMethod); + return new UnaryResult(future); + } + + async Task> InvokeAsyncCore(string path, TRequest request, Func requestMethod) + { + if (this.option.Headers == null && filters.Length != 0) + { + // always creating new Metadata is bad manner for performance + this.option = this.option.WithHeaders(new Metadata()); + } + + var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); + var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); + var result = response as ResponseContext; + if (result != null) + { + return result; + } + else + { + throw new InvalidOperationException("ResponseContext is null."); + } + } + + protected async Task> InvokeTaskAsync(string path, TRequest request, Func requestMethod) + { + if (this.option.Headers == null && filters.Length != 0) + { + // always creating new Metadata is bad manner for performance + this.option = this.option.WithHeaders(new Metadata()); + } + + var requestContext = new RequestContext(request, this, path, option, typeof(TResponse), filters, requestMethod); + var response = await InterceptInvokeHelper.InvokeWithFilter(requestContext); + var result = response as ResponseContext; + if (result != null) + { + return new UnaryResult(Task.FromResult(result)); + } + else + { + throw new InvalidOperationException("ResponseContext is null."); + } } protected abstract MagicOnionClientBase Clone(); diff --git a/src/MagicOnion/UnaryResult.cs b/src/MagicOnion/UnaryResult.cs index 28b05d41b..2c00d4946 100644 --- a/src/MagicOnion/UnaryResult.cs +++ b/src/MagicOnion/UnaryResult.cs @@ -1,6 +1,7 @@ using Grpc.Core; +using MagicOnion.Client; using MagicOnion.CompilerServices; // require this using in AsyncMethodBuilder -using MessagePack; +using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -18,16 +19,14 @@ public struct UnaryResult internal readonly TResponse rawValue; // internal internal readonly Task rawTaskValue; // internal - readonly AsyncUnaryCall inner; - readonly IFormatterResolver resolver; + readonly Task> response; public UnaryResult(TResponse rawValue) { this.hasRawValue = true; this.rawValue = rawValue; this.rawTaskValue = null; - this.inner = null; - this.resolver = null; + this.response = null; } public UnaryResult(Task rawTaskValue) @@ -35,23 +34,15 @@ public UnaryResult(Task rawTaskValue) this.hasRawValue = true; this.rawValue = default(TResponse); this.rawTaskValue = rawTaskValue; - this.inner = null; - this.resolver = null; + this.response = null; } - public UnaryResult(AsyncUnaryCall inner, IFormatterResolver resolver) + public UnaryResult(Task> response) { this.hasRawValue = false; this.rawValue = default(TResponse); this.rawTaskValue = null; - this.inner = inner; - this.resolver = resolver; - } - - async Task Deserialize() - { - var bytes = await inner.ResponseAsync.ConfigureAwait(false); - return LZ4MessagePackSerializer.Deserialize(bytes, resolver); + this.response = response; } /// @@ -63,7 +54,7 @@ public Task ResponseAsync { if (!hasRawValue) { - return Deserialize(); + return UnwrapResponse(); } else if (rawTaskValue != null) { @@ -83,10 +74,44 @@ public Task ResponseHeadersAsync { get { - return inner.ResponseHeadersAsync; + return UnwrapResponseHeaders(); + } + } + + async Task UnwrapResponse() + { + var ctx = await response.ConfigureAwait(false); + return await ctx.ResponseAsync.ConfigureAwait(false); + } + + async Task UnwrapResponseHeaders() + { + var ctx = await response.ConfigureAwait(false); + return await ctx.ResponseHeadersAsync.ConfigureAwait(false); + } + + async void UnwrapDispose() + { + try + { + var ctx = await response.ConfigureAwait(false); + ctx.Dispose(); + } + catch + { } } + ResponseContext TryUnwrap() + { + if (!response.IsCompleted) + { + throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this."); + } + + return response.Result; + } + /// /// Allows awaiting this object directly. /// @@ -101,7 +126,7 @@ public TaskAwaiter GetAwaiter() /// public Status GetStatus() { - return inner.GetStatus(); + return TryUnwrap().GetStatus(); } /// @@ -110,7 +135,7 @@ public Status GetStatus() /// public Metadata GetTrailers() { - return inner.GetTrailers(); + return TryUnwrap().GetTrailers(); } /// @@ -125,7 +150,14 @@ public Metadata GetTrailers() /// public void Dispose() { - inner.Dispose(); + if (!response.IsCompleted) + { + UnwrapDispose(); + } + else + { + response.Result.Dispose(); + } } } } \ No newline at end of file diff --git a/src/MagicOnion/Utils/DynamicAssembly.cs b/src/MagicOnion/Utils/DynamicAssembly.cs index dc83b8e0a..ebc5cfd57 100644 --- a/src/MagicOnion/Utils/DynamicAssembly.cs +++ b/src/MagicOnion/Utils/DynamicAssembly.cs @@ -6,7 +6,12 @@ namespace MagicOnion.Utils { - internal class DynamicAssembly +#if ENABLE_SAVE_ASSEMBLY + public +#else + internal +#endif + class DynamicAssembly { readonly object gate = new object(); diff --git a/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs index f1fd6d048..cbf66efa8 100644 --- a/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs +++ b/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs @@ -317,7 +317,8 @@ public UnaryResult Unary1(int x, int y, string z = "unknown") var callResult = invoker.AsyncUnaryCall(method, null, default(CallOptions), request); - return new UnaryResult(callResult, MessagePackSerializer.DefaultResolver); + var response = new ResponseContext(callResult, MessagePackSerializer.DefaultResolver); + return new UnaryResult(Task.FromResult(response)); } public UnaryResult Unary2(MyRequest req) diff --git a/tests/MagicOnion.NetCoreTests/Tests/ClientFilterTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ClientFilterTest.cs new file mode 100644 index 000000000..d08d1d3d3 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/ClientFilterTest.cs @@ -0,0 +1,230 @@ +#pragma warning disable CS1998 + +using Grpc.Core; +using FluentAssertions; +using MagicOnion.Client; +using MagicOnion.Server; +using MagicOnion.Tests; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; +using System.Diagnostics; +using MessagePack; + +namespace MagicOnion.NetCoreTests.Tests +{ + + public interface IClientFilterTestService : IService + { + UnaryResult Unary1(int x, int y); + UnaryResult HeaderEcho(); + UnaryResult AlwaysError(); + } + + public class ClientFilterTestService : ServiceBase, IClientFilterTestService + { + + public async UnaryResult Unary1(int x, int y) + { + return x + y; + } + + public UnaryResult HeaderEcho() + { + foreach (var item in Context.CallContext.RequestHeaders) + { + Context.CallContext.ResponseTrailers.Add(item); + } + + return ReturnNil(); + } + + public UnaryResult AlwaysError() + { + throw new Exception("Ok, throw error!"); + } + } + + public class CountFilter : IClientFilter + { + public int CalledCount; + + public int FilterCount; + + public CountFilter(int count) + { + this.FilterCount = count; + } + + public async ValueTask SendAsync(RequestContext context, Func> next) + { + CalledCount++; + var response = (await next(context)).As(); + var newResult = await response.ResponseAsync; + return response.WithNewResult(newResult + 10); + } + } + + public class AppendHeaderFilter : IClientFilter + { + public async ValueTask SendAsync(RequestContext context, Func> next) + { + var header = context.CallOptions.Headers; + header.Add("x-foo", "abcdefg"); + header.Add("x-bar", "hijklmn"); + + var response = await next(context); + return response; + } + } + + public class LoggingFilter : IClientFilter + { + public async ValueTask SendAsync(RequestContext context, Func> next) + { + Console.WriteLine("Request Begin:" + context.MethodPath); + + var sw = Stopwatch.StartNew(); + var response = await next(context); + sw.Stop(); + + Console.WriteLine("Request Completed:" + context.MethodPath + ", Elapsed:" + sw.Elapsed.TotalMilliseconds + "ms"); + + return response; + } + } + + public class ResponseHandlingFilter : IClientFilter + { + public async ValueTask SendAsync(RequestContext context, Func> next) + { + var response = await next(context); + + if (context.MethodPath == "ICalc/Sum") + { + var sumResult = await response.GetResponseAs(); + Console.WriteLine("Called Sum, Result:" + sumResult); + } + + return response; + } + } + + public class MockRequestFilter : IClientFilter + { + public async ValueTask SendAsync(RequestContext context, Func> next) + { + if (context.MethodPath == "ICalc/Sum") + { + // don't call next, return mock result. + return new ResponseContext(9999); + } + + return await next(context); + } + } + + public class RetryFilter : IClientFilter + { + public async ValueTask SendAsync(RequestContext context, Func> next) + { + Exception lastException = null; + var retryCount = 0; + while (retryCount != 3) + { + try + { + // using same CallOptions so be careful to add duplicate headers or etc. + return await next(context); + } + catch (Exception ex) + { + lastException = ex; + } + retryCount++; + } + + throw new RetryFailedException(retryCount, lastException); + } + } + + + public class RetryFailedException : Exception + { + public int RetryCount { get; } + public Exception LastException { get; } + + public RetryFailedException(int retryCount, Exception lastException) + { + this.RetryCount = retryCount; + this.LastException = lastException; + } + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class ClientFilterTest + { + ITestOutputHelper logger; + Channel channel; + + public ClientFilterTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.channel = server.DefaultChannel; + } + + [Fact] + public async Task SimpleFilter() + { + for (int i = 1; i <= 20; i++) + { + var filters = Enumerable.Range(0, i).Select(_ => new CountFilter(i)).ToArray(); + var client = MagicOnionClient.Create(channel, filters); + var r0 = await client.Unary1(1000, 2000); + r0.Should().Be(3000 + 10 * i); + + foreach (var item in filters) + { + item.CalledCount.Should().Be(1); + } + } + } + + [Fact] + public async Task HeaderEcho() + { + var res = MagicOnionClient.Create(channel, new IClientFilter[] + { + new AppendHeaderFilter(), + new RetryFilter() + }).HeaderEcho(); + await res; + var meta1 = res.GetTrailers()[0]; + meta1.Key.Should().Be("x-foo"); + meta1.Value.Should().Be("abcdefg"); + var meta2 = res.GetTrailers()[1]; + meta2.Key.Should().Be("x-bar"); + meta2.Value.Should().Be("hijklmn"); + } + + [Fact] + public async Task ErrorRetry() + { + var ex = await Assert.ThrowsAsync(async () => + { + var filter = new RetryFilter(); + await MagicOnionClient.Create(channel, new IClientFilter[] + { + filter + }).AlwaysError(); + }); + + ex.RetryCount.Should().Be(3); + logger.WriteLine(ex.LastException.ToString()); + } + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs b/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs index 18ae6ab2d..21553c2ca 100644 --- a/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs +++ b/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs @@ -137,7 +137,8 @@ public async Task Unary() var r = await client.Unary1(10, 20); r.Should().Be(30); - var r2 = await await client.Unary1Task(1000, 2000); + var r0 = await client.Unary1Task(1000, 2000); + var r2 = await r0; r2.Should().Be(3000); }