Initial Client/Server Communication
#1 Followed the MagicOnion quickstart guide, borrowing code from the chat sample app to get something working
This commit is contained in:
parent
52b0d4d8ff
commit
a0c8dbee72
493 changed files with 57790 additions and 3 deletions
8
Assets/Scripts/MagicOnion.Abstractions/Client.meta
Normal file
8
Assets/Scripts/MagicOnion.Abstractions/Client.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aca11e9577c2fb24c9ff99ccac34dbba
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Grpc.Core;
|
||||
using MessagePack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion.Client
|
||||
{
|
||||
public interface IResponseContext : IDisposable
|
||||
{
|
||||
Task<Metadata> ResponseHeadersAsync { get; }
|
||||
Status GetStatus();
|
||||
Metadata GetTrailers();
|
||||
Type ResponseType { get; }
|
||||
}
|
||||
|
||||
public interface IResponseContext<T> : IResponseContext
|
||||
{
|
||||
Task<T> ResponseAsync { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d1954ea03abc7c34fba72fdd2cd9a8bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
132
Assets/Scripts/MagicOnion.Abstractions/ClientStreamingResult.cs
Normal file
132
Assets/Scripts/MagicOnion.Abstractions/ClientStreamingResult.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using Grpc.Core;
|
||||
using MessagePack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapped AsyncClientStreamingCall.
|
||||
/// </summary>
|
||||
public struct ClientStreamingResult<TRequest, TResponse> : IDisposable
|
||||
{
|
||||
internal readonly TResponse rawValue;
|
||||
internal readonly bool hasRawValue;
|
||||
readonly AsyncClientStreamingCall<byte[], byte[]> inner;
|
||||
readonly IClientStreamWriter<TRequest> requestStream;
|
||||
readonly MessagePackSerializerOptions serializerOptions;
|
||||
|
||||
public ClientStreamingResult(TResponse rawValue)
|
||||
{
|
||||
this.hasRawValue = true;
|
||||
this.rawValue = rawValue;
|
||||
this.inner = null;
|
||||
this.requestStream = null;
|
||||
this.serializerOptions = null;
|
||||
}
|
||||
|
||||
public ClientStreamingResult(AsyncClientStreamingCall<byte[], byte[]> inner, IClientStreamWriter<TRequest> requestStream, MessagePackSerializerOptions serializerOptions)
|
||||
{
|
||||
this.hasRawValue = false;
|
||||
this.rawValue = default(TResponse);
|
||||
this.inner = inner;
|
||||
this.requestStream = requestStream;
|
||||
this.serializerOptions = serializerOptions;
|
||||
}
|
||||
|
||||
async Task<TResponse> Deserialize()
|
||||
{
|
||||
var bytes = await inner.ResponseAsync.ConfigureAwait(false);
|
||||
return MessagePackSerializer.Deserialize<TResponse>(bytes, serializerOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous call result.
|
||||
/// </summary>
|
||||
public Task<TResponse> ResponseAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
if (hasRawValue)
|
||||
{
|
||||
return Task.FromResult(rawValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Deserialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous access to response headers.
|
||||
/// </summary>
|
||||
public Task<Metadata> ResponseHeadersAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.inner.ResponseHeadersAsync;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async stream to send streaming requests.
|
||||
/// </summary>
|
||||
public IClientStreamWriter<TRequest> RequestStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.requestStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows awaiting this object directly.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TaskAwaiter<TResponse> GetAwaiter()
|
||||
{
|
||||
return ResponseAsync.GetAwaiter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call status if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Status GetStatus()
|
||||
{
|
||||
return this.inner.GetStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call trailing metadata if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Metadata GetTrailers()
|
||||
{
|
||||
return this.inner.GetTrailers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides means to cleanup after the call.
|
||||
/// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
|
||||
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
|
||||
/// As a result, all resources being used by the call should be released eventually.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally, there is no need for you to dispose the call unless you want to utilize the
|
||||
/// "Cancel" semantics of invoking <c>Dispose</c>.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.inner != null)
|
||||
{
|
||||
this.inner.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e92d02db742a454fa25774f217806cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cee65f2126c31d0418b29c422566e23b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
|
||||
namespace MagicOnion.CompilerServices
|
||||
{
|
||||
public struct AsyncUnaryResultMethodBuilder<T>
|
||||
{
|
||||
private AsyncTaskMethodBuilder<T> methodBuilder;
|
||||
private T result;
|
||||
private bool haveResult;
|
||||
private bool useBuilder;
|
||||
|
||||
public static AsyncUnaryResultMethodBuilder<T> Create()
|
||||
{
|
||||
return new AsyncUnaryResultMethodBuilder<T>() { methodBuilder = AsyncTaskMethodBuilder<T>.Create() };
|
||||
}
|
||||
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics
|
||||
}
|
||||
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
methodBuilder.SetStateMachine(stateMachine);
|
||||
}
|
||||
|
||||
public void SetResult(T result)
|
||||
{
|
||||
if (useBuilder)
|
||||
{
|
||||
methodBuilder.SetResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.result = result;
|
||||
haveResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
methodBuilder.SetException(exception);
|
||||
}
|
||||
|
||||
public UnaryResult<T> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
if (haveResult)
|
||||
{
|
||||
return new UnaryResult<T>(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
useBuilder = true;
|
||||
return new UnaryResult<T>(methodBuilder.Task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : INotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
useBuilder = true;
|
||||
methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||
where TAwaiter : ICriticalNotifyCompletion
|
||||
where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
useBuilder = true;
|
||||
methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 975c42469777f414fae46186971f0dc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
using Grpc.Core;
|
||||
using MessagePack;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapped AsyncDuplexStreamingCall.
|
||||
/// </summary>
|
||||
public struct DuplexStreamingResult<TRequest, TResponse> : IDisposable
|
||||
{
|
||||
readonly AsyncDuplexStreamingCall<byte[], byte[]> inner;
|
||||
readonly IClientStreamWriter<TRequest> requestStream;
|
||||
readonly IAsyncStreamReader<TResponse> responseStream;
|
||||
|
||||
public DuplexStreamingResult(AsyncDuplexStreamingCall<byte[], byte[]> inner, IClientStreamWriter<TRequest> requestStream, IAsyncStreamReader<TResponse> responseStream, MessagePackSerializerOptions serializerOptions)
|
||||
{
|
||||
this.inner = inner;
|
||||
this.requestStream = requestStream;
|
||||
this.responseStream = responseStream;
|
||||
}
|
||||
|
||||
public AsyncDuplexStreamingCall<byte[], byte[]> RawStreamingCall => inner;
|
||||
|
||||
/// <summary>
|
||||
/// Async stream to read streaming responses.
|
||||
/// </summary>
|
||||
public IAsyncStreamReader<TResponse> ResponseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return responseStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async stream to send streaming requests.
|
||||
/// </summary>
|
||||
public IClientStreamWriter<TRequest> RequestStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return requestStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous access to response headers.
|
||||
/// </summary>
|
||||
public Task<Metadata> ResponseHeadersAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.inner.ResponseHeadersAsync;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call status if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Status GetStatus()
|
||||
{
|
||||
return this.inner.GetStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call trailing metadata if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Metadata GetTrailers()
|
||||
{
|
||||
return this.inner.GetTrailers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides means to cleanup after the call.
|
||||
/// If the call has already finished normally (request stream has been completed and response stream has been fully read), doesn't do anything.
|
||||
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
|
||||
/// As a result, all resources being used by the call should be released eventually.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally, there is no need for you to dispose the call unless you want to utilize the
|
||||
/// "Cancel" semantics of invoking <c>Dispose</c>.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.inner != null)
|
||||
{
|
||||
this.inner.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 383e96f82d4d6244b951f0fc83d49b63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// instruction for moc.exe, surround #if symbol with output code.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
|
||||
public class GenerateDefineDebugAttribute : Attribute
|
||||
{
|
||||
public GenerateDefineDebugAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab20e13a7e0b6b644a42dbfa3b44afd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/Scripts/MagicOnion.Abstractions/IService.cs
Normal file
21
Assets/Scripts/MagicOnion.Abstractions/IService.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Grpc.Core;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
// used for MagicOnionEngine assembly scan for boostup analyze speed.
|
||||
public interface IServiceMarker
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IService<TSelf> : IServiceMarker
|
||||
{
|
||||
TSelf WithOptions(CallOptions option);
|
||||
TSelf WithHeaders(Metadata headers);
|
||||
TSelf WithDeadline(DateTime deadline);
|
||||
TSelf WithCancellationToken(CancellationToken cancellationToken);
|
||||
TSelf WithHost(string host);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MagicOnion.Abstractions/IService.cs.meta
Normal file
11
Assets/Scripts/MagicOnion.Abstractions/IService.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 56677a2edb7ea644faf4846ad283b007
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/Scripts/MagicOnion.Abstractions/IStreamingHub.cs
Normal file
15
Assets/Scripts/MagicOnion.Abstractions/IStreamingHub.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
public interface IStreamingHubMarker
|
||||
{
|
||||
}
|
||||
|
||||
public interface IStreamingHub<TSelf, TReceiver> : IStreamingHubMarker, IServiceMarker
|
||||
{
|
||||
TSelf FireAndForget(); // if changed to get-only property, intellisense does not work on VS2017.
|
||||
Task DisposeAsync();
|
||||
Task WaitForDisconnect();
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MagicOnion.Abstractions/IStreamingHub.cs.meta
Normal file
11
Assets/Scripts/MagicOnion.Abstractions/IStreamingHub.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d0a5fcfa0834b84ea6ff4d77e478fb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Assets/Scripts/MagicOnion.Abstractions/IgnoreAttribute.cs
Normal file
16
Assets/Scripts/MagicOnion.Abstractions/IgnoreAttribute.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't register on MagicOnionEngine.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
|
||||
public class IgnoreAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d13f2ed066fd874b8749ca248f42c51
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "MagicOnion.Abstractions",
|
||||
"references": [
|
||||
"MessagePack"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 62b9d94cd08d5d047b266d42e8684cb8
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/MagicOnion.Abstractions/Server.meta
Normal file
8
Assets/Scripts/MagicOnion.Abstractions/Server.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c1f828ada0daa0843bfef8cc6ebda1bf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/MagicOnion.Abstractions/Server/Hubs.meta
Normal file
8
Assets/Scripts/MagicOnion.Abstractions/Server/Hubs.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33588a48f556fba45b238f907a8ebc38
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MagicOnion.Server.Hubs
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class MethodIdAttribute : Attribute
|
||||
{
|
||||
public readonly int MethodId;
|
||||
|
||||
public MethodIdAttribute(int methodId)
|
||||
{
|
||||
MethodId = methodId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1752261f18dd5f749b27216b7cbe81e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
using Grpc.Core;
|
||||
using MessagePack;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapped AsyncServerStreamingCall.
|
||||
/// </summary>
|
||||
public struct ServerStreamingResult<TResponse> : IDisposable
|
||||
{
|
||||
readonly AsyncServerStreamingCall<byte[]> inner;
|
||||
readonly IAsyncStreamReader<TResponse> responseStream;
|
||||
|
||||
public ServerStreamingResult(AsyncServerStreamingCall<byte[]> inner, IAsyncStreamReader<TResponse> responseStream, MessagePackSerializerOptions serializerOptions)
|
||||
{
|
||||
this.inner = inner;
|
||||
this.responseStream = responseStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async stream to read streaming responses.
|
||||
/// </summary>
|
||||
public IAsyncStreamReader<TResponse> ResponseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return responseStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous access to response headers.
|
||||
/// </summary>
|
||||
public Task<Metadata> ResponseHeadersAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.inner.ResponseHeadersAsync;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call status if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Status GetStatus()
|
||||
{
|
||||
return this.inner.GetStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call trailing metadata if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Metadata GetTrailers()
|
||||
{
|
||||
return this.inner.GetTrailers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides means to cleanup after the call.
|
||||
/// If the call has already finished normally (response stream has been fully read), doesn't do anything.
|
||||
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
|
||||
/// As a result, all resources being used by the call should be released eventually.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally, there is no need for you to dispose the call unless you want to utilize the
|
||||
/// "Cancel" semantics of invoking <c>Dispose</c>.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.inner != null)
|
||||
{
|
||||
this.inner.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 46a21c751ab9a4f4caf74e806bfe2bea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
163
Assets/Scripts/MagicOnion.Abstractions/UnaryResult.cs
Normal file
163
Assets/Scripts/MagicOnion.Abstractions/UnaryResult.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using Grpc.Core;
|
||||
using MagicOnion.Client;
|
||||
using MagicOnion.CompilerServices; // require this using in AsyncMethodBuilder
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MagicOnion
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapped AsyncUnaryCall.
|
||||
/// </summary>
|
||||
#if NON_UNITY || (CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6)))
|
||||
[AsyncMethodBuilder(typeof(AsyncUnaryResultMethodBuilder<>))]
|
||||
#endif
|
||||
public struct UnaryResult<TResponse>
|
||||
{
|
||||
internal readonly bool hasRawValue; // internal
|
||||
internal readonly TResponse rawValue; // internal
|
||||
internal readonly Task<TResponse> rawTaskValue; // internal
|
||||
|
||||
readonly Task<IResponseContext<TResponse>> response;
|
||||
|
||||
public UnaryResult(TResponse rawValue)
|
||||
{
|
||||
this.hasRawValue = true;
|
||||
this.rawValue = rawValue;
|
||||
this.rawTaskValue = null;
|
||||
this.response = null;
|
||||
}
|
||||
|
||||
public UnaryResult(Task<TResponse> rawTaskValue)
|
||||
{
|
||||
this.hasRawValue = true;
|
||||
this.rawValue = default(TResponse);
|
||||
this.rawTaskValue = rawTaskValue;
|
||||
this.response = null;
|
||||
}
|
||||
|
||||
public UnaryResult(Task<IResponseContext<TResponse>> response)
|
||||
{
|
||||
this.hasRawValue = false;
|
||||
this.rawValue = default(TResponse);
|
||||
this.rawTaskValue = null;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous call result.
|
||||
/// </summary>
|
||||
public Task<TResponse> ResponseAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!hasRawValue)
|
||||
{
|
||||
return UnwrapResponse();
|
||||
}
|
||||
else if (rawTaskValue != null)
|
||||
{
|
||||
return rawTaskValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(rawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous access to response headers.
|
||||
/// </summary>
|
||||
public Task<Metadata> ResponseHeadersAsync
|
||||
{
|
||||
get
|
||||
{
|
||||
return UnwrapResponseHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
async Task<TResponse> UnwrapResponse()
|
||||
{
|
||||
var ctx = await response.ConfigureAwait(false);
|
||||
return await ctx.ResponseAsync.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
async Task<Metadata> 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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
IResponseContext<TResponse> TryUnwrap()
|
||||
{
|
||||
if (!response.IsCompleted)
|
||||
{
|
||||
throw new InvalidOperationException("UnaryResult request is not yet completed, please await before call this.");
|
||||
}
|
||||
|
||||
return response.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows awaiting this object directly.
|
||||
/// </summary>
|
||||
public TaskAwaiter<TResponse> GetAwaiter()
|
||||
{
|
||||
return ResponseAsync.GetAwaiter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call status if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Status GetStatus()
|
||||
{
|
||||
return TryUnwrap().GetStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the call trailing metadata if the call has already finished.
|
||||
/// Throws InvalidOperationException otherwise.
|
||||
/// </summary>
|
||||
public Metadata GetTrailers()
|
||||
{
|
||||
return TryUnwrap().GetTrailers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides means to cleanup after the call.
|
||||
/// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything.
|
||||
/// Otherwise, requests cancellation of the call which should terminate all pending async operations associated with the call.
|
||||
/// As a result, all resources being used by the call should be released eventually.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally, there is no need for you to dispose the call unless you want to utilize the
|
||||
/// "Cancel" semantics of invoking <c>Dispose</c>.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!response.IsCompleted)
|
||||
{
|
||||
UnwrapDispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Result.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/MagicOnion.Abstractions/UnaryResult.cs.meta
Normal file
11
Assets/Scripts/MagicOnion.Abstractions/UnaryResult.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cc0fe0c16ad63fd489a131a76e925b08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in a new issue