JSObjectReference API review follow-up (#25476)

* API review changes.

* Fixed JSObjectReferenceJsonConverter

* CR feedback

* Update JSObjectReferenceExtensions.cs
This commit is contained in:
Mackinnon Buck 2020-09-02 15:09:26 -07:00 committed by GitHub
parent 6ec1b48118
commit 2a0b7dc43e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 286 additions and 99 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@ -12,4 +12,8 @@
<Reference Include="Microsoft.JSInterop" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(SharedSourceRoot)JSInterop\*.cs" />
</ItemGroup>
</Project>

View File

@ -78,7 +78,7 @@ namespace Microsoft.JSInterop.WebAssembly
var callInfo = new JSCallInfo
{
FunctionIdentifier = identifier,
ResultType = ResultTypeFromGeneric<TResult>()
ResultType = JSCallResultTypeHelper.FromGeneric<TResult>()
};
var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out var exception, ref callInfo, arg0, arg1, arg2);

View File

@ -137,7 +137,7 @@
ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke<Segment[]>("returnArray").Select(x => x.Source).ToArray());
}
var jsObjectReference = await JSRuntime.InvokeAsync<JSObjectReference>("returnJSObjectReference");
var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync<string>("identity", "Invoked from JSObjectReference");
ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync<int>("nested.add", 2, 3)).ToString();
ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync<int>("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString();
@ -151,7 +151,7 @@
JSObjectReferenceInvokeNonFunctionException = e;
}
var module = await JSRuntime.InvokeAsync<JSObjectReference>("import", "./js/testmodule.js");
var module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/testmodule.js");
ReturnValues["jsObjectReferenceModule"] = await module.InvokeAsync<string>("identity", "Returned from module!");
if (shouldSupportSyncInterop)
@ -192,7 +192,7 @@
{
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
var jsInProcObjectReference = inProcRuntime.Invoke<JSInProcessObjectReference>("returnJSObjectReference");
var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
}

View File

@ -415,20 +415,20 @@ namespace BasicTestApp.InteropTest
}
[JSInvokable]
public static JSObjectReference RoundTripJSObjectReference(JSObjectReference jsObjectReference)
public static IJSObjectReference RoundTripJSObjectReference(IJSObjectReference jsObjectReference)
{
return jsObjectReference;
}
[JSInvokable]
public static async Task<JSObjectReference> RoundTripJSObjectReferenceAsync(JSObjectReference jSObjectReference)
public static async Task<IJSObjectReference> RoundTripJSObjectReferenceAsync(IJSObjectReference jSObjectReference)
{
await Task.Yield();
return jSObjectReference;
}
[JSInvokable]
public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectReference jsObjectReference)
public static string InvokeDisposedJSObjectReferenceException(IJSInProcessObjectReference jsObjectReference)
{
try
{
@ -442,7 +442,7 @@ namespace BasicTestApp.InteropTest
}
[JSInvokable]
public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(JSObjectReference jsObjectReference)
public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(IJSObjectReference jsObjectReference)
{
try
{

View File

@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
/// </summary>
public interface IJSInProcessObjectReference : IJSObjectReference
{
/// <summary>
/// Invokes the specified JavaScript function synchronously.
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
TValue Invoke<TValue>(string identifier, params object?[]? args);
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object.
/// </summary>
public interface IJSObjectReference : IAsyncDisposable
{
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// <para>
/// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different, or no timeout,
/// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
/// </para>
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args);
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="cancellationToken">
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
/// </param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
}
}

View File

@ -7,17 +7,21 @@ using System.Text.Json.Serialization;
namespace Microsoft.JSInterop.Infrastructure
{
internal sealed class JSObjectReferenceJsonConverter<TJSObjectReference>
: JsonConverter<TJSObjectReference> where TJSObjectReference : JSObjectReference
internal sealed class JSObjectReferenceJsonConverter<TInterface, TImplementation> : JsonConverter<TInterface>
where TInterface : class, IJSObjectReference
where TImplementation : JSObjectReference, TInterface
{
private readonly Func<long, TJSObjectReference> _jsObjectReferenceFactory;
private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
public JSObjectReferenceJsonConverter(Func<long, TJSObjectReference> jsObjectReferenceFactory)
public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
{
_jsObjectReferenceFactory = jsObjectReferenceFactory;
}
public override TJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
public override bool CanConvert(Type typeToConvert)
=> typeToConvert == typeof(TInterface) || typeToConvert == typeof(TImplementation);
public override TInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
long id = -1;
@ -49,10 +53,10 @@ namespace Microsoft.JSInterop.Infrastructure
return _jsObjectReferenceFactory(id);
}
public override void Write(Utf8JsonWriter writer, TJSObjectReference value, JsonSerializerOptions options)
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteNumber(JSObjectReference.IdKey, value.Id);
writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id);
writer.WriteEndObject();
}
}

View File

@ -5,10 +5,7 @@ using System.Diagnostics.CodeAnalysis;
namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
/// </summary>
public class JSInProcessObjectReference : JSObjectReference
internal class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
{
private readonly JSInProcessRuntime _jsRuntime;
@ -17,15 +14,8 @@ namespace Microsoft.JSInterop
_jsRuntime = jsRuntime;
}
/// <summary>
/// Invokes the specified JavaScript function synchronously.
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
[return: MaybeNull]
public TValue Invoke<TValue>(string identifier, params object[] args)
public TValue Invoke<TValue>(string identifier, params object?[]? args)
{
ThrowIfDisposed();

View File

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.JSInterop
{
public static class JSInProcessObjectReferenceExtensions
{
/// <summary>
/// Invokes the specified JavaScript function synchronously.
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSInProcessObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
public static void InvokeVoid(this IJSInProcessObjectReference jsObjectReference, string identifier, params object?[] args)
{
if (jsObjectReference == null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
jsObjectReference.Invoke<object>(identifier, args);
}
}
}

View File

@ -17,7 +17,7 @@ namespace Microsoft.JSInterop
/// </summary>
protected JSInProcessRuntime()
{
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<JSInProcessObjectReference>(
JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
id => new JSInProcessObjectReference(this, id)));
}
@ -27,7 +27,7 @@ namespace Microsoft.JSInterop
var resultJson = InvokeJS(
identifier,
JsonSerializer.Serialize(args, JsonSerializerOptions),
ResultTypeFromGeneric<TValue>(),
JSCallResultTypeHelper.FromGeneric<TValue>(),
targetInstanceId);
if (resultJson is null)

View File

@ -8,77 +8,37 @@ using System.Threading.Tasks;
namespace Microsoft.JSInterop
{
/// <summary>
/// Represents a reference to a JavaScript object.
/// </summary>
public class JSObjectReference : IAsyncDisposable
internal class JSObjectReference : IJSObjectReference
{
internal static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId");
public static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId");
private readonly JSRuntime _jsRuntime;
private bool _disposed;
internal long Id { get; }
public long Id { get; }
internal JSObjectReference(JSRuntime jsRuntime, long id)
public JSObjectReference(JSRuntime jsRuntime, long id)
{
_jsRuntime = jsRuntime;
Id = id;
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
public async ValueTask InvokeVoidAsync(string identifier, params object[] args)
{
await InvokeAsync<object>(identifier, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// <para>
/// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different, or no timeout,
/// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
/// </para>
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, params object[] args)
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
{
ThrowIfDisposed();
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="cancellationToken">
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
/// </param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, params object[] args)
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
{
ThrowIfDisposed();
return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
}
/// <summary>
/// Disposes the <see cref="JSObjectReference"/>, freeing its resources and disabling it from further use.
/// </summary>
/// <returns>A <see cref="ValueTask"/> representing the completion of the operation.</returns>
public async ValueTask DisposeAsync()
{
if (!_disposed)
@ -89,9 +49,6 @@ namespace Microsoft.JSInterop
}
}
/// <summary>
/// Throws an exception if this instance has been disposed.
/// </summary>
protected void ThrowIfDisposed()
{
if (_disposed)

View File

@ -0,0 +1,139 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.JSInterop
{
/// <summary>
/// Extensions for <see cref="IJSObjectReference"/>.
/// </summary>
public static class JSObjectReferenceExtensions
{
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
await jsObjectReference.InvokeAsync<object>(identifier, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// <para>
/// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different timeout, or no timeout,
/// consider using <see cref="IJSObjectReference.InvokeAsync{TValue}(string, CancellationToken, object[])" />.
/// </para>
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
return jsObjectReference.InvokeAsync<TValue>(identifier, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="cancellationToken">
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
/// </param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
return jsObjectReference.InvokeAsync<TValue>(identifier, cancellationToken, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="cancellationToken">
/// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
/// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
/// </param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
await jsObjectReference.InvokeAsync<object>(identifier, cancellationToken, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
public static async ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout);
var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None;
return await jsObjectReference.InvokeAsync<TValue>(identifier, cancellationToken, args);
}
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// </summary>
/// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
/// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
/// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
/// <param name="args">JSON-serializable arguments.</param>
/// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
{
if (jsObjectReference is null)
{
throw new ArgumentNullException(nameof(jsObjectReference));
}
using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout);
var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None;
await jsObjectReference.InvokeAsync<object>(identifier, cancellationToken, args);
}
}
}

View File

@ -37,7 +37,7 @@ namespace Microsoft.JSInterop
Converters =
{
new DotNetObjectReferenceJsonConverterFactory(this),
new JSObjectReferenceJsonConverter<JSObjectReference>(id => new JSObjectReference(this, id)),
new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(id => new JSObjectReference(this, id)),
}
};
}
@ -52,17 +52,6 @@ namespace Microsoft.JSInterop
/// </summary>
protected TimeSpan? DefaultAsyncTimeout { get; set; }
/// <summary>
/// Creates a <see cref="JSCallResultType"/> from the given generic type.
/// </summary>
/// <typeparam name="TResult">
/// The type of the result of the relevant JS interop call.
/// </typeparam>
protected static JSCallResultType ResultTypeFromGeneric<TResult>()
=> typeof(TResult) == typeof(JSObjectReference) || typeof(TResult) == typeof(JSInProcessObjectReference) ?
JSCallResultType.JSObjectReference :
JSCallResultType.Default;
/// <summary>
/// Invokes the specified JavaScript function asynchronously.
/// <para>
@ -134,7 +123,7 @@ namespace Microsoft.JSInterop
var argsJson = args?.Any() == true ?
JsonSerializer.Serialize(args, JsonSerializerOptions) :
null;
var resultType = ResultTypeFromGeneric<TValue>();
var resultType = JSCallResultTypeHelper.FromGeneric<TValue>();
BeginInvokeJS(taskId, identifier, argsJson, resultType, targetInstanceId);

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@ -13,6 +13,10 @@
<Reference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(SharedSourceRoot)JSInterop\*.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.JSInterop.Tests" />
</ItemGroup>

View File

@ -18,7 +18,7 @@ namespace Microsoft.JSInterop.Infrastructure
var json = "{}";
// Act & Assert
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
Assert.Equal("Required property __jsObjectId not found.", ex.Message);
}
@ -29,7 +29,7 @@ namespace Microsoft.JSInterop.Infrastructure
var json = "{\"foo\":2}";
// Act & Assert
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
Assert.Equal("Unexcepted JSON property foo.", ex.Message);
}
@ -40,7 +40,7 @@ namespace Microsoft.JSInterop.Infrastructure
var json = $"{{\"__jsObjectId\":5";
// Act & Assert
var ex = Record.Exception(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
var ex = Record.Exception(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
Assert.IsAssignableFrom<JsonException>(ex);
}
@ -51,7 +51,7 @@ namespace Microsoft.JSInterop.Infrastructure
var json = $"{{\"__jsObjectId\":3,\"__jsObjectId\":7}}";
// Act & Assert
var ex = Record.Exception(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
var ex = Record.Exception(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
Assert.IsAssignableFrom<JsonException>(ex);
}
@ -63,7 +63,7 @@ namespace Microsoft.JSInterop.Infrastructure
var json = $"{{\"__jsObjectId\":{expectedId}}}";
// Act
var deserialized = JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions)!;
var deserialized = (JSObjectReference)JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions)!;
// Assert
Assert.Equal(expectedId, deserialized?.Id);
@ -76,7 +76,7 @@ namespace Microsoft.JSInterop.Infrastructure
var jsObjectRef = new JSObjectReference(JSRuntime, 7);
// Act
var json = JsonSerializer.Serialize(jsObjectRef, JsonSerializerOptions);
var json = JsonSerializer.Serialize((IJSObjectReference)jsObjectRef, JsonSerializerOptions);
// Assert
Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);

View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.JSInterop
{
internal static class JSCallResultTypeHelper
{
public static JSCallResultType FromGeneric<TResult>()
=> typeof(TResult) == typeof(IJSObjectReference) || typeof(TResult) == typeof(IJSInProcessObjectReference) ?
JSCallResultType.JSObjectReference :
JSCallResultType.Default;
}
}