Fixed streaming hub method with ValueTask (#1572)
This commit is contained in:
parent
d941a4be09
commit
d6178f2482
|
|
@ -116,8 +116,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
|
||||||
public override IReadOnlyList<Type> GetParameterTypes(string methodName)
|
public override IReadOnlyList<Type> GetParameterTypes(string methodName)
|
||||||
{
|
{
|
||||||
HubMethodDescriptor descriptor;
|
if (!_methods.TryGetValue(methodName, out var descriptor))
|
||||||
if (!_methods.TryGetValue(methodName, out descriptor))
|
|
||||||
{
|
{
|
||||||
return Type.EmptyTypes;
|
return Type.EmptyTypes;
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +164,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await ValidateInvocationMode(methodExecutor.MethodReturnType, isStreamedInvocation, hubMethodInvocationMessage, connection))
|
if (!await ValidateInvocationMode(methodExecutor, isStreamedInvocation, hubMethodInvocationMessage, connection))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +187,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
|
||||||
if (isStreamedInvocation)
|
if (isStreamedInvocation)
|
||||||
{
|
{
|
||||||
if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, methodExecutor, result, methodExecutor.MethodReturnType, out var enumerator))
|
if (!TryGetStreamingEnumerator(connection, hubMethodInvocationMessage.InvocationId, methodExecutor, result, out var enumerator))
|
||||||
{
|
{
|
||||||
Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);
|
Log.InvalidReturnValueFromStreamingMethod(_logger, methodExecutor.MethodInfo.Name);
|
||||||
|
|
||||||
|
|
@ -341,10 +340,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
return authorizationResult.Succeeded;
|
return authorizationResult.Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ValidateInvocationMode(Type resultType, bool isStreamedInvocation,
|
private async Task<bool> ValidateInvocationMode(ObjectMethodExecutor methodExecutor, bool isStreamedInvocation,
|
||||||
HubMethodInvocationMessage hubMethodInvocationMessage, HubConnectionContext connection)
|
HubMethodInvocationMessage hubMethodInvocationMessage, HubConnectionContext connection)
|
||||||
{
|
{
|
||||||
var isStreamedResult = IsStreamed(resultType);
|
var isStreamedResult = IsStreamed(methodExecutor);
|
||||||
if (isStreamedResult && !isStreamedInvocation)
|
if (isStreamedResult && !isStreamedInvocation)
|
||||||
{
|
{
|
||||||
// Non-null/empty InvocationId? Blocking
|
// Non-null/empty InvocationId? Blocking
|
||||||
|
|
@ -370,11 +369,13 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsStreamed(Type resultType)
|
private static bool IsStreamed(ObjectMethodExecutor methodExecutor)
|
||||||
{
|
{
|
||||||
// TODO: cache reflection for performance, on HubMethodDescriptor maybe?
|
var resultType = (methodExecutor.IsMethodAsync)
|
||||||
resultType = UnwrapTask(resultType);
|
? methodExecutor.AsyncResultType
|
||||||
|
: methodExecutor.MethodReturnType;
|
||||||
|
|
||||||
|
// TODO: cache reflection for performance, on HubMethodDescriptor maybe?
|
||||||
var observableInterface = IsIObservable(resultType) ?
|
var observableInterface = IsIObservable(resultType) ?
|
||||||
resultType :
|
resultType :
|
||||||
resultType.GetInterfaces().FirstOrDefault(IsIObservable);
|
resultType.GetInterfaces().FirstOrDefault(IsIObservable);
|
||||||
|
|
@ -392,13 +393,15 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetStreamingEnumerator(HubConnectionContext connection, string invocationId, ObjectMethodExecutor methodExecutor, object result, Type resultType, out IAsyncEnumerator<object> enumerator)
|
private bool TryGetStreamingEnumerator(HubConnectionContext connection, string invocationId, ObjectMethodExecutor methodExecutor, object result, out IAsyncEnumerator<object> enumerator)
|
||||||
{
|
{
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
// TODO: cache reflection for performance, on HubMethodDescriptor maybe?
|
var resultType = (methodExecutor.IsMethodAsync)
|
||||||
resultType = UnwrapTask(resultType);
|
? methodExecutor.AsyncResultType
|
||||||
|
: methodExecutor.MethodReturnType;
|
||||||
|
|
||||||
|
// TODO: cache reflection for performance, on HubMethodDescriptor maybe?
|
||||||
var observableInterface = IsIObservable(resultType) ?
|
var observableInterface = IsIObservable(resultType) ?
|
||||||
resultType :
|
resultType :
|
||||||
resultType.GetInterfaces().FirstOrDefault(IsIObservable);
|
resultType.GetInterfaces().FirstOrDefault(IsIObservable);
|
||||||
|
|
@ -426,16 +429,6 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Type UnwrapTask(Type type)
|
|
||||||
{
|
|
||||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
|
||||||
{
|
|
||||||
return type.GetGenericArguments()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsIObservable(Type iface)
|
private static bool IsIObservable(Type iface)
|
||||||
{
|
{
|
||||||
return iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IObservable<>);
|
return iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IObservable<>);
|
||||||
|
|
|
||||||
|
|
@ -519,6 +519,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils
|
||||||
return CounterObservable(count);
|
return CounterObservable(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask<IObservable<string>> CounterObservableValueTaskAsync(int count)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
return CounterObservable(count);
|
||||||
|
}
|
||||||
|
|
||||||
public ChannelReader<string> CounterChannel(int count)
|
public ChannelReader<string> CounterChannel(int count)
|
||||||
{
|
{
|
||||||
var channel = Channel.CreateUnbounded<string>();
|
var channel = Channel.CreateUnbounded<string>();
|
||||||
|
|
@ -541,6 +547,12 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils
|
||||||
return CounterChannel(count);
|
return CounterChannel(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask<ChannelReader<string>> CounterChannelValueTaskAsync(int count)
|
||||||
|
{
|
||||||
|
await Task.Yield();
|
||||||
|
return CounterChannel(count);
|
||||||
|
}
|
||||||
|
|
||||||
public ChannelReader<string> BlockingStream()
|
public ChannelReader<string> BlockingStream()
|
||||||
{
|
{
|
||||||
return Channel.CreateUnbounded<string>().Reader;
|
return Channel.CreateUnbounded<string>().Reader;
|
||||||
|
|
|
||||||
|
|
@ -1453,7 +1453,11 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
foreach (var method in new[] { nameof(StreamingHub.CounterChannel), nameof(StreamingHub.CounterChannelAsync), nameof(StreamingHub.CounterObservable), nameof(StreamingHub.CounterObservableAsync) })
|
foreach (var method in new[]
|
||||||
|
{
|
||||||
|
nameof(StreamingHub.CounterChannel), nameof(StreamingHub.CounterChannelAsync), nameof(StreamingHub.CounterChannelValueTaskAsync),
|
||||||
|
nameof(StreamingHub.CounterObservable), nameof(StreamingHub.CounterObservableAsync), nameof(StreamingHub.CounterObservableValueTaskAsync)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
foreach (var protocol in new IHubProtocol[] { new JsonHubProtocol(), new MessagePackHubProtocol() })
|
foreach (var protocol in new IHubProtocol[] { new JsonHubProtocol(), new MessagePackHubProtocol() })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue