Merge pull request #13007 from dotnet-maestro-bot/merge/release/3.0-to-master

[automated] Merge branch 'release/3.0' => 'master'
This commit is contained in:
Justin Kotalik 2019-08-09 17:41:48 -07:00 committed by GitHub
commit 8c02467b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
164 changed files with 2294 additions and 1600 deletions

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Keep this file in sync with src/ProjectTemplates/test/Infrastructure/NuGet.config.in. -->
<configuration>
<packageSources>
<clear />

View File

@ -65,6 +65,7 @@ Later on, this will be checked using this condition:
</PropertyGroup>
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.2.7' ">
<PackagesInPatch>
Microsoft.AspNetCore.Hosting;
</PackagesInPatch>
</PropertyGroup>
</Project>

View File

@ -65,16 +65,10 @@ namespace Microsoft.AspNetCore.Blazor.Http
}
namespace Microsoft.AspNetCore.Blazor.Rendering
{
public partial class WebAssemblyRenderer : Microsoft.AspNetCore.Components.Rendering.Renderer
public static partial class WebAssemblyEventDispatcher
{
public WebAssemblyRenderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) : base (default(System.IServiceProvider), default(Microsoft.Extensions.Logging.ILoggerFactory)) { }
public override Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get { throw null; } }
public System.Threading.Tasks.Task AddComponentAsync(System.Type componentType, string domElementSelector) { throw null; }
public System.Threading.Tasks.Task AddComponentAsync<TComponent>(string domElementSelector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; }
public override System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, System.EventArgs eventArgs) { throw null; }
protected override void Dispose(bool disposing) { }
protected override void HandleException(System.Exception exception) { }
protected override System.Threading.Tasks.Task UpdateDisplayAsync(in Microsoft.AspNetCore.Components.Rendering.RenderBatch batch) { throw null; }
[Microsoft.JSInterop.JSInvokableAttribute("DispatchEvent")]
public static System.Threading.Tasks.Task DispatchEvent(Microsoft.AspNetCore.Components.Web.WebEventDescriptor eventDescriptor, string eventArgsJson) { throw null; }
}
}
namespace Microsoft.AspNetCore.Components.Builder

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@ -11,4 +11,10 @@
<Reference Include="Microsoft.AspNetCore.Components.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\Shared\src\BrowserNavigationManagerInterop.cs" />
<Compile Include="..\..\..\Shared\src\JsonSerializerOptionsProvider.cs" />
<Compile Include="..\..\..\Shared\src\WebEventData.cs" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,46 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Blazor.Rendering
{
internal static class RendererRegistry
{
// In case there are multiple concurrent Blazor renderers in the same .NET WebAssembly
// process, we track them by ID. This allows events to be dispatched to the correct one,
// as well as rooting them for GC purposes, since nothing would otherwise be referencing
// them even though we might still receive incoming events from JS.
private static int _nextId;
private static Dictionary<int, WebAssemblyRenderer> _renderers = new Dictionary<int, WebAssemblyRenderer>();
internal static WebAssemblyRenderer Find(int rendererId)
{
return _renderers.ContainsKey(rendererId)
? _renderers[rendererId]
: throw new ArgumentException($"There is no renderer with ID {rendererId}.");
}
public static int Add(WebAssemblyRenderer renderer)
{
var id = _nextId++;
_renderers.Add(id, renderer);
return id;
}
public static bool TryRemove(int rendererId)
{
if (_renderers.ContainsKey(rendererId))
{
_renderers.Remove(rendererId);
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,30 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Blazor.Rendering
{
/// <summary>
/// Dispatches events from JavaScript to a Blazor WebAssembly renderer.
/// Intended for internal use only.
/// </summary>
public static class WebAssemblyEventDispatcher
{
/// <summary>
/// For framework use only.
/// </summary>
[JSInvokable(nameof(DispatchEvent))]
public static Task DispatchEvent(WebEventDescriptor eventDescriptor, string eventArgsJson)
{
var webEvent = WebEventData.Parse(eventDescriptor, eventArgsJson);
var renderer = RendererRegistry.Find(eventDescriptor.BrowserRendererId);
return renderer.DispatchEventAsync(
webEvent.EventHandlerId,
webEvent.EventFieldInfo,
webEvent.EventArgs);
}
}
}

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Blazor.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.Extensions.Logging;
@ -16,7 +15,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
/// Provides mechanisms for rendering <see cref="IComponent"/> instances in a
/// web browser, dispatching events to them, and refreshing the UI as required.
/// </summary>
public class WebAssemblyRenderer : Renderer
internal class WebAssemblyRenderer : Renderer
{
private readonly int _webAssemblyRendererId;
@ -31,10 +30,8 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
: base(serviceProvider, loggerFactory)
{
// The browser renderer registers and unregisters itself with the static
// registry. This works well with the WebAssembly runtime, and is simple for the
// case where Blazor is running in process.
_webAssemblyRendererId = RendererRegistry.Current.Add(this);
// The WebAssembly renderer registers and unregisters itself with the static registry
_webAssemblyRendererId = RendererRegistry.Add(this);
}
public override Dispatcher Dispatcher => NullDispatcher.Instance;
@ -77,9 +74,9 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
WebAssemblyJSRuntime.Instance.Invoke<object>(
"Blazor._internal.attachRootComponentToElement",
_webAssemblyRendererId,
domElementSelector,
componentId);
componentId,
_webAssemblyRendererId);
return RenderRootComponentAsync(componentId);
}
@ -88,7 +85,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
RendererRegistry.Current.TryRemove(_webAssemblyRendererId);
RendererRegistry.TryRemove(_webAssemblyRendererId);
}
/// <inheritdoc />

View File

@ -4,7 +4,7 @@
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2098127">brief survey</a>
<a target="_blank" class="font-weight-bold" href="https://go.microsoft.com/fwlink/?linkid=2100553">brief survey</a>
</span>
and tell us what you think.
</div>

View File

@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Components
public CascadingParameterAttribute() { }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
public partial class CascadingValue<T> : Microsoft.AspNetCore.Components.IComponent
public partial class CascadingValue<TValue> : Microsoft.AspNetCore.Components.IComponent
{
public CascadingValue() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
@ -124,7 +124,7 @@ namespace Microsoft.AspNetCore.Components
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public T Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }
public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
}
@ -188,17 +188,17 @@ namespace Microsoft.AspNetCore.Components
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Func<object, System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback Create(object receiver, System.Func<System.Threading.Tasks.Task> callback) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> CreateInferred<T>(object receiver, System.Action<T> callback, T value) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> CreateInferred<TValue>(object receiver, System.Action<TValue> callback, TValue value) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> CreateInferred<T>(object receiver, System.Func<T, System.Threading.Tasks.Task> callback, T value) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> CreateInferred<TValue>(object receiver, System.Func<TValue, System.Threading.Tasks.Task> callback, TValue value) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, Microsoft.AspNetCore.Components.EventCallback callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, Microsoft.AspNetCore.Components.EventCallback callback) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, Microsoft.AspNetCore.Components.EventCallback<T> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Action callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Action<T> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Func<System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<T> Create<T>(object receiver, System.Func<T, System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, Microsoft.AspNetCore.Components.EventCallback<TValue> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, System.Action callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, System.Action<TValue> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, System.Func<System.Threading.Tasks.Task> callback) { throw null; }
public Microsoft.AspNetCore.Components.EventCallback<TValue> Create<TValue>(object receiver, System.Func<TValue, System.Threading.Tasks.Task> callback) { throw null; }
}
public static partial class EventCallbackFactoryBinderExtensions
{
@ -241,13 +241,13 @@ namespace Microsoft.AspNetCore.Components
public System.Threading.Tasks.Task InvokeAsync(object arg) { throw null; }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct EventCallback<T>
public readonly partial struct EventCallback<TValue>
{
private readonly object _dummy;
public static readonly Microsoft.AspNetCore.Components.EventCallback<T> Empty;
public static readonly Microsoft.AspNetCore.Components.EventCallback<TValue> Empty;
public EventCallback(Microsoft.AspNetCore.Components.IHandleEvent receiver, System.MulticastDelegate @delegate) { throw null; }
public bool HasDelegate { get { throw null; } }
public System.Threading.Tasks.Task InvokeAsync(T arg) { throw null; }
public System.Threading.Tasks.Task InvokeAsync(TValue arg) { throw null; }
}
public partial interface IComponent
{
@ -365,11 +365,11 @@ namespace Microsoft.AspNetCore.Components
public static Microsoft.AspNetCore.Components.ParameterView Empty { get { throw null; } }
public static Microsoft.AspNetCore.Components.ParameterView FromDictionary(System.Collections.Generic.IDictionary<string, object> parameters) { throw null; }
public Microsoft.AspNetCore.Components.ParameterView.Enumerator GetEnumerator() { throw null; }
public T GetValueOrDefault<T>(string parameterName) { throw null; }
public T GetValueOrDefault<T>(string parameterName, T defaultValue) { throw null; }
public TValue GetValueOrDefault<TValue>(string parameterName) { throw null; }
public TValue GetValueOrDefault<TValue>(string parameterName, TValue defaultValue) { throw null; }
public void SetParameterProperties(object target) { }
public System.Collections.Generic.IReadOnlyDictionary<string, object> ToDictionary() { throw null; }
public bool TryGetValue<T>(string parameterName, out T result) { throw null; }
public bool TryGetValue<TValue>(string parameterName, out TValue result) { throw null; }
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct Enumerator
{
@ -380,7 +380,7 @@ namespace Microsoft.AspNetCore.Components
}
}
public delegate void RenderFragment(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder);
public delegate Microsoft.AspNetCore.Components.RenderFragment RenderFragment<T>(T value);
public delegate Microsoft.AspNetCore.Components.RenderFragment RenderFragment<TValue>(TValue value);
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct RenderHandle
{
@ -466,7 +466,7 @@ namespace Microsoft.AspNetCore.Components.Forms
public FieldIdentifier(object model, string fieldName) { throw null; }
public string FieldName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public object Model { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public static Microsoft.AspNetCore.Components.Forms.FieldIdentifier Create<T>(System.Linq.Expressions.Expression<System.Func<T>> accessor) { throw null; }
public static Microsoft.AspNetCore.Components.Forms.FieldIdentifier Create<TField>(System.Linq.Expressions.Expression<System.Func<TField>> accessor) { throw null; }
public bool Equals(Microsoft.AspNetCore.Components.Forms.FieldIdentifier otherIdentifier) { throw null; }
public override bool Equals(object obj) { throw null; }
public override int GetHashCode() { throw null; }
@ -556,13 +556,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
public void AddAttribute(int sequence, string name, System.MulticastDelegate value) { }
public void AddAttribute(int sequence, string name, object value) { }
public void AddAttribute(int sequence, string name, string value) { }
public void AddAttribute<T>(int sequence, string name, Microsoft.AspNetCore.Components.EventCallback<T> value) { }
public void AddAttribute<TArgument>(int sequence, string name, Microsoft.AspNetCore.Components.EventCallback<TArgument> value) { }
public void AddComponentReferenceCapture(int sequence, System.Action<object> componentReferenceCaptureAction) { }
public void AddContent(int sequence, Microsoft.AspNetCore.Components.MarkupString markupContent) { }
public void AddContent(int sequence, Microsoft.AspNetCore.Components.RenderFragment fragment) { }
public void AddContent(int sequence, object textContent) { }
public void AddContent(int sequence, string textContent) { }
public void AddContent<T>(int sequence, Microsoft.AspNetCore.Components.RenderFragment<T> fragment, T value) { }
public void AddContent<TValue>(int sequence, Microsoft.AspNetCore.Components.RenderFragment<TValue> fragment, TValue value) { }
public void AddElementReferenceCapture(int sequence, System.Action<Microsoft.AspNetCore.Components.ElementReference> elementReferenceCaptureAction) { }
public void AddMarkupContent(int sequence, string markupContent) { }
public void AddMultipleAttributes(int sequence, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>> attributes) { }

View File

@ -2,7 +2,7 @@
@implements IDisposable
@inject AuthenticationStateProvider AuthenticationStateProvider
<CascadingValue T="Task<AuthenticationState>" Value="@_currentAuthenticationStateTask" ChildContent="@ChildContent" />
<CascadingValue TValue="Task<AuthenticationState>" Value="@_currentAuthenticationStateTask" ChildContent="@ChildContent" />
@code {
private Task<AuthenticationState> _currentAuthenticationStateTask;

View File

@ -5,14 +5,13 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// A component that provides a cascading value to all descendant components.
/// </summary>
public class CascadingValue<T> : ICascadingValueComponent, IComponent
public class CascadingValue<TValue> : ICascadingValueComponent, IComponent
{
private RenderHandle _renderHandle;
private HashSet<ComponentState> _subscribers; // Lazily instantiated
@ -26,7 +25,7 @@ namespace Microsoft.AspNetCore.Components
/// <summary>
/// The value to be provided.
/// </summary>
[Parameter] public T Value { get; set; }
[Parameter] public TValue Value { get; set; }
/// <summary>
/// Optionally gives a name to the provided value. Descendant components
@ -74,7 +73,7 @@ namespace Microsoft.AspNetCore.Components
{
if (parameter.Name.Equals(nameof(Value), StringComparison.OrdinalIgnoreCase))
{
Value = (T)parameter.Value;
Value = (TValue)parameter.Value;
hasSuppliedValue = true;
}
else if (parameter.Name.Equals(nameof(ChildContent), StringComparison.OrdinalIgnoreCase))
@ -86,7 +85,7 @@ namespace Microsoft.AspNetCore.Components
Name = (string)parameter.Value;
if (string.IsNullOrEmpty(Name))
{
throw new ArgumentException($"The parameter '{nameof(Name)}' for component '{nameof(CascadingValue<T>)}' does not allow null or empty values.");
throw new ArgumentException($"The parameter '{nameof(Name)}' for component '{nameof(CascadingValue<TValue>)}' does not allow null or empty values.");
}
}
else if (parameter.Name.Equals(nameof(IsFixed), StringComparison.OrdinalIgnoreCase))
@ -95,7 +94,7 @@ namespace Microsoft.AspNetCore.Components
}
else
{
throw new ArgumentException($"The component '{nameof(CascadingValue<T>)}' does not accept a parameter with the name '{parameter.Name}'.");
throw new ArgumentException($"The component '{nameof(CascadingValue<TValue>)}' does not accept a parameter with the name '{parameter.Name}'.");
}
}
@ -136,7 +135,7 @@ namespace Microsoft.AspNetCore.Components
bool ICascadingValueComponent.CanSupplyValue(Type requestedType, string requestedName)
{
if (!requestedType.IsAssignableFrom(typeof(T)))
if (!requestedType.IsAssignableFrom(typeof(TValue)))
{
return false;
}

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Components
internal readonly IHandleEvent Receiver;
/// <summary>
/// Creates the new <see cref="EventCallback{T}"/>.
/// Creates the new <see cref="EventCallback"/>.
/// </summary>
/// <param name="receiver">The event receiver.</param>
/// <param name="delegate">The delegate to bind.</param>
@ -66,73 +66,4 @@ namespace Microsoft.AspNetCore.Components
return RequiresExplicitReceiver ? (object)this : Delegate;
}
}
/// <summary>
/// A bound event handler delegate.
/// </summary>
public readonly struct EventCallback<T> : IEventCallback
{
/// <summary>
/// Gets an empty <see cref="EventCallback{T}"/>.
/// </summary>
public static readonly EventCallback<T> Empty = new EventCallback<T>(null, (Action)(() => { }));
internal readonly MulticastDelegate Delegate;
internal readonly IHandleEvent Receiver;
/// <summary>
/// Creates the new <see cref="EventCallback{T}"/>.
/// </summary>
/// <param name="receiver">The event receiver.</param>
/// <param name="delegate">The delegate to bind.</param>
public EventCallback(IHandleEvent receiver, MulticastDelegate @delegate)
{
Receiver = receiver;
Delegate = @delegate;
}
/// <summary>
/// Gets a value that indicates whether the delegate associated with this event dispatcher is non-null.
/// </summary>
public bool HasDelegate => Delegate != null;
// This is a hint to the runtime that Receiver is a different object than what
// Delegate.Target points to. This allows us to avoid boxing the command object
// when building the render tree. See logic where this is used.
internal bool RequiresExplicitReceiver => Receiver != null && !object.ReferenceEquals(Receiver, Delegate?.Target);
/// <summary>
/// Invokes the delegate associated with this binding and dispatches an event notification to the
/// appropriate component.
/// </summary>
/// <param name="arg">The argument.</param>
/// <returns>A <see cref="Task"/> which completes asynchronously once event processing has completed.</returns>
public Task InvokeAsync(T arg)
{
if (Receiver == null)
{
return EventCallbackWorkItem.InvokeAsync<T>(Delegate, arg);
}
return Receiver.HandleEventAsync(new EventCallbackWorkItem(Delegate), arg);
}
internal EventCallback AsUntyped()
{
return new EventCallback(Receiver ?? Delegate?.Target as IHandleEvent, Delegate);
}
object IEventCallback.UnpackForRenderTree()
{
return RequiresExplicitReceiver ? (object)AsUntyped() : Delegate;
}
}
// Used to understand boxed generic EventCallbacks
internal interface IEventCallback
{
bool HasDelegate { get; }
object UnpackForRenderTree();
}
}

View File

@ -105,14 +105,14 @@ namespace Microsoft.AspNetCore.Components
/// <param name="callback"></param>
/// <returns></returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public EventCallback<T> Create<T>(object receiver, EventCallback callback)
public EventCallback<TValue> Create<TValue>(object receiver, EventCallback callback)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
return new EventCallback<T>(callback.Receiver, callback.Delegate);
return new EventCallback<TValue>(callback.Receiver, callback.Delegate);
}
/// <summary>
@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Components
/// <param name="callback"></param>
/// <returns></returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public EventCallback<T> Create<T>(object receiver, EventCallback<T> callback)
public EventCallback<TValue> Create<TValue>(object receiver, EventCallback<TValue> callback)
{
if (receiver == null)
{
@ -139,14 +139,14 @@ namespace Microsoft.AspNetCore.Components
/// <param name="receiver">The event receiver.</param>
/// <param name="callback">The event callback.</param>
/// <returns>The <see cref="EventCallback"/>.</returns>
public EventCallback<T> Create<T>(object receiver, Action callback)
public EventCallback<TValue> Create<TValue>(object receiver, Action callback)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
return CreateCore<T>(receiver, callback);
return CreateCore<TValue>(receiver, callback);
}
/// <summary>
@ -156,14 +156,14 @@ namespace Microsoft.AspNetCore.Components
/// <param name="receiver">The event receiver.</param>
/// <param name="callback">The event callback.</param>
/// <returns>The <see cref="EventCallback"/>.</returns>
public EventCallback<T> Create<T>(object receiver, Action<T> callback)
public EventCallback<TValue> Create<TValue>(object receiver, Action<TValue> callback)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
return CreateCore<T>(receiver, callback);
return CreateCore<TValue>(receiver, callback);
}
/// <summary>
@ -173,14 +173,14 @@ namespace Microsoft.AspNetCore.Components
/// <param name="receiver">The event receiver.</param>
/// <param name="callback">The event callback.</param>
/// <returns>The <see cref="EventCallback"/>.</returns>
public EventCallback<T> Create<T>(object receiver, Func<Task> callback)
public EventCallback<TValue> Create<TValue>(object receiver, Func<Task> callback)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
return CreateCore<T>(receiver, callback);
return CreateCore<TValue>(receiver, callback);
}
/// <summary>
@ -190,14 +190,14 @@ namespace Microsoft.AspNetCore.Components
/// <param name="receiver">The event receiver.</param>
/// <param name="callback">The event callback.</param>
/// <returns>The <see cref="EventCallback"/>.</returns>
public EventCallback<T> Create<T>(object receiver, Func<T, Task> callback)
public EventCallback<TValue> Create<TValue>(object receiver, Func<TValue, Task> callback)
{
if (receiver == null)
{
throw new ArgumentNullException(nameof(receiver));
}
return CreateCore<T>(receiver, callback);
return CreateCore<TValue>(receiver, callback);
}
/// <summary>
@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Components
/// <param name="value"></param>
/// <returns></returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public EventCallback<T> CreateInferred<T>(object receiver, Action<T> callback, T value)
public EventCallback<TValue> CreateInferred<TValue>(object receiver, Action<TValue> callback, TValue value)
{
return Create(receiver, callback);
}
@ -223,7 +223,7 @@ namespace Microsoft.AspNetCore.Components
/// <param name="value"></param>
/// <returns></returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public EventCallback<T> CreateInferred<T>(object receiver, Func<T, Task> callback, T value)
public EventCallback<TValue> CreateInferred<TValue>(object receiver, Func<TValue, Task> callback, TValue value)
{
return Create(receiver, callback);
}
@ -233,9 +233,9 @@ namespace Microsoft.AspNetCore.Components
return new EventCallback(callback?.Target as IHandleEvent ?? receiver as IHandleEvent, callback);
}
private EventCallback<T> CreateCore<T>(object receiver, MulticastDelegate callback)
private EventCallback<TValue> CreateCore<TValue>(object receiver, MulticastDelegate callback)
{
return new EventCallback<T>(callback?.Target as IHandleEvent ?? receiver as IHandleEvent, callback);
return new EventCallback<TValue>(callback?.Target as IHandleEvent ?? receiver as IHandleEvent, callback);
}
}
}

View File

@ -0,0 +1,69 @@
// 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.Tasks;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// A bound event handler delegate.
/// </summary>
public readonly struct EventCallback<TValue> : IEventCallback
{
/// <summary>
/// Gets an empty <see cref="EventCallback{TValue}"/>.
/// </summary>
public static readonly EventCallback<TValue> Empty = new EventCallback<TValue>(null, (Action)(() => { }));
internal readonly MulticastDelegate Delegate;
internal readonly IHandleEvent Receiver;
/// <summary>
/// Creates the new <see cref="EventCallback{TValue}"/>.
/// </summary>
/// <param name="receiver">The event receiver.</param>
/// <param name="delegate">The delegate to bind.</param>
public EventCallback(IHandleEvent receiver, MulticastDelegate @delegate)
{
Receiver = receiver;
Delegate = @delegate;
}
/// <summary>
/// Gets a value that indicates whether the delegate associated with this event dispatcher is non-null.
/// </summary>
public bool HasDelegate => Delegate != null;
// This is a hint to the runtime that Receiver is a different object than what
// Delegate.Target points to. This allows us to avoid boxing the command object
// when building the render tree. See logic where this is used.
internal bool RequiresExplicitReceiver => Receiver != null && !object.ReferenceEquals(Receiver, Delegate?.Target);
/// <summary>
/// Invokes the delegate associated with this binding and dispatches an event notification to the
/// appropriate component.
/// </summary>
/// <param name="arg">The argument.</param>
/// <returns>A <see cref="Task"/> which completes asynchronously once event processing has completed.</returns>
public Task InvokeAsync(TValue arg)
{
if (Receiver == null)
{
return EventCallbackWorkItem.InvokeAsync<TValue>(Delegate, arg);
}
return Receiver.HandleEventAsync(new EventCallbackWorkItem(Delegate), arg);
}
internal EventCallback AsUntyped()
{
return new EventCallback(Receiver ?? Delegate?.Target as IHandleEvent, Delegate);
}
object IEventCallback.UnpackForRenderTree()
{
return RequiresExplicitReceiver ? (object)AsUntyped() : Delegate;
}
}
}

View File

@ -16,7 +16,8 @@ namespace Microsoft.AspNetCore.Components.Forms
/// Initializes a new instance of the <see cref="FieldIdentifier"/> structure.
/// </summary>
/// <param name="accessor">An expression that identifies an object member.</param>
public static FieldIdentifier Create<T>(Expression<Func<T>> accessor)
/// <typeparam name="TField">The field <see cref="Type"/>.</typeparam>
public static FieldIdentifier Create<TField>(Expression<Func<TField>> accessor)
{
if (accessor == null)
{

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.AspNetCore.Components
{
// Used to understand boxed generic EventCallbacks
internal interface IEventCallback
{
bool HasDelegate { get; }
object UnpackForRenderTree();
}
}

View File

@ -53,17 +53,17 @@ namespace Microsoft.AspNetCore.Components
/// <summary>
/// Gets the value of the parameter with the specified name.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="parameterName">The name of the parameter.</param>
/// <param name="result">Receives the result, if any.</param>
/// <returns>True if a matching parameter was found; false otherwise.</returns>
public bool TryGetValue<T>(string parameterName, out T result)
public bool TryGetValue<TValue>(string parameterName, out TValue result)
{
foreach (var entry in this)
{
if (string.Equals(entry.Name, parameterName))
{
result = (T)entry.Value;
result = (TValue)entry.Value;
return true;
}
}
@ -76,22 +76,22 @@ namespace Microsoft.AspNetCore.Components
/// Gets the value of the parameter with the specified name, or a default value
/// if no such parameter exists in the collection.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="parameterName">The name of the parameter.</param>
/// <returns>The parameter value if found; otherwise the default value for the specified type.</returns>
public T GetValueOrDefault<T>(string parameterName)
=> GetValueOrDefault<T>(parameterName, default);
public TValue GetValueOrDefault<TValue>(string parameterName)
=> GetValueOrDefault<TValue>(parameterName, default);
/// <summary>
/// Gets the value of the parameter with the specified name, or a specified default value
/// if no such parameter exists in the collection.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="parameterName">The name of the parameter.</param>
/// <param name="defaultValue">The default value to return if no such parameter exists in the collection.</param>
/// <returns>The parameter value if found; otherwise <paramref name="defaultValue"/>.</returns>
public T GetValueOrDefault<T>(string parameterName, T defaultValue)
=> TryGetValue<T>(parameterName, out T result) ? result : defaultValue;
public TValue GetValueOrDefault<TValue>(string parameterName, TValue defaultValue)
=> TryGetValue<TValue>(parameterName, out TValue result) ? result : defaultValue;
/// <summary>
/// Returns a dictionary populated with the contents of the <see cref="ParameterView"/>.

View File

@ -13,10 +13,10 @@ namespace Microsoft.AspNetCore.Components
public delegate void RenderFragment(RenderTreeBuilder builder);
/// <summary>
/// Represents a segment of UI content for an object of type <typeparamref name="T"/>, implemented as
/// Represents a segment of UI content for an object of type <typeparamref name="TValue"/>, implemented as
/// a function that returns a <see cref="RenderFragment"/>.
/// </summary>
/// <typeparam name="T">The type of object.</typeparam>
/// <typeparam name="TValue">The type of object.</typeparam>
/// <param name="value">The value used to build the content.</param>
public delegate RenderFragment RenderFragment<T>(T value);
public delegate RenderFragment RenderFragment<TValue>(TValue value);
}

View File

@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
/// <param name="fragment">Content to append.</param>
/// <param name="value">The value used by <paramref name="fragment"/>.</param>
public void AddContent<T>(int sequence, RenderFragment<T> fragment, T value)
public void AddContent<TValue>(int sequence, RenderFragment<TValue> fragment, TValue value)
{
if (fragment != null)
{
@ -281,7 +281,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// This method is provided for infrastructure purposes, and is used to support generated code
/// that uses <see cref="EventCallbackFactory"/>.
/// </remarks>
public void AddAttribute<T>(int sequence, string name, EventCallback<T> value)
public void AddAttribute<TArgument>(int sequence, string name, EventCallback<TArgument> value)
{
AssertCanAddAttribute();
if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Web;
@ -41,7 +40,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
}
JSInterop.JSRuntime.SetCurrentJSRuntime(circuitHost.JSRuntime);
RendererRegistry.SetCurrentRendererRegistry(circuitHost.RendererRegistry);
}
public event UnhandledExceptionEventHandler UnhandledException;
@ -50,7 +48,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
string circuitId,
IServiceScope scope,
CircuitClientProxy client,
RendererRegistry rendererRegistry,
RemoteRenderer renderer,
IReadOnlyList<ComponentDescriptor> descriptors,
RemoteJSRuntime jsRuntime,
@ -60,7 +57,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
CircuitId = circuitId;
_scope = scope ?? throw new ArgumentNullException(nameof(scope));
Client = client;
RendererRegistry = rendererRegistry ?? throw new ArgumentNullException(nameof(rendererRegistry));
Descriptors = descriptors ?? throw new ArgumentNullException(nameof(descriptors));
Renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
JSRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
@ -85,8 +81,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
public RemoteRenderer Renderer { get; }
public RendererRegistry RendererRegistry { get; }
public IReadOnlyList<ComponentDescriptor> Descriptors { get; }
public IServiceProvider Services { get; }
@ -137,46 +131,38 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
}
}
public async Task DispatchEvent(string eventDescriptorJson, string eventArgs)
public async Task DispatchEvent(string eventDescriptorJson, string eventArgsJson)
{
RendererRegistryEventDispatcher.BrowserEventDescriptor eventDescriptor = null;
WebEventData webEventData;
try
{
AssertInitialized();
eventDescriptor = ParseEventDescriptor(eventDescriptorJson);
if (eventDescriptor == null)
{
return;
}
webEventData = WebEventData.Parse(eventDescriptorJson, eventArgsJson);
}
catch (Exception ex)
{
Log.DispatchEventFailedToParseEventData(_logger, ex);
return;
}
try
{
await Renderer.Dispatcher.InvokeAsync(() =>
{
SetCurrentCircuitHost(this);
return RendererRegistryEventDispatcher.DispatchEvent(eventDescriptor, eventArgs);
return Renderer.DispatchEventAsync(
webEventData.EventHandlerId,
webEventData.EventFieldInfo,
webEventData.EventArgs);
});
}
catch (Exception ex)
{
Log.DispatchEventFailedToDispatchEvent(_logger, eventDescriptor != null ? eventDescriptor.EventHandlerId.ToString() : null, ex);
Log.DispatchEventFailedToDispatchEvent(_logger, webEventData.EventHandlerId.ToString(), ex);
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
}
}
private RendererRegistryEventDispatcher.BrowserEventDescriptor ParseEventDescriptor(string eventDescriptorJson)
{
try
{
return JsonSerializer.Deserialize<RendererRegistryEventDispatcher.BrowserEventDescriptor>(
eventDescriptorJson,
JsonSerializerOptionsProvider.Options);
}
catch (Exception ex)
{
Log.DispatchEventFailedToParseEventDescriptor(_logger, ex);
return null;
}
}
public async Task InitializeAsync(CancellationToken cancellationToken)
{
await Renderer.Dispatcher.InvokeAsync(async () =>
@ -214,11 +200,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
try
{
AssertInitialized();
if (assemblyName == "Microsoft.AspNetCore.Components.Web" && methodIdentifier == "DispatchEvent")
{
Log.DispatchEventTroughJSInterop(_logger);
return;
}
await Renderer.Dispatcher.InvokeAsync(() =>
{
@ -410,9 +391,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
private static readonly Action<ILogger, Exception> _endInvokeDispatchException;
private static readonly Action<ILogger, long, string, Exception> _endInvokeJSFailed;
private static readonly Action<ILogger, long, Exception> _endInvokeJSSucceeded;
private static readonly Action<ILogger, Exception> _dispatchEventFailedToParseEventDescriptor;
private static readonly Action<ILogger, Exception> _dispatchEventFailedToParseEventData;
private static readonly Action<ILogger, string, Exception> _dispatchEventFailedToDispatchEvent;
private static readonly Action<ILogger, Exception> _dispatchEventThroughJSInterop;
private static readonly Action<ILogger, string, string, Exception> _locationChange;
private static readonly Action<ILogger, string, string, Exception> _locationChangeSucceeded;
private static readonly Action<ILogger, string, string, Exception> _locationChangeFailed;
@ -426,7 +406,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
public static readonly EventId OnConnectionDown = new EventId(104, "OnConnectionDown");
public static readonly EventId OnCircuitClosed = new EventId(105, "OnCircuitClosed");
public static readonly EventId InvalidBrowserEventFormat = new EventId(106, "InvalidBrowserEventFormat");
public static readonly EventId DispatchEventFailedToParseEventDescriptor = new EventId(107, "DispatchEventFailedToParseEventDescriptor");
public static readonly EventId DispatchEventFailedToParseEventData = new EventId(107, "DispatchEventFailedToParseEventData");
public static readonly EventId DispatchEventFailedToDispatchEvent = new EventId(108, "DispatchEventFailedToDispatchEvent");
public static readonly EventId BeginInvokeDotNet = new EventId(109, "BeginInvokeDotNet");
public static readonly EventId EndInvokeDispatchException = new EventId(110, "EndInvokeDispatchException");
@ -495,21 +475,16 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
EventIds.EndInvokeJSSucceeded,
"The JS interop call with callback id '{AsyncCall}' succeeded.");
_dispatchEventFailedToParseEventDescriptor = LoggerMessage.Define(
_dispatchEventFailedToParseEventData = LoggerMessage.Define(
LogLevel.Debug,
EventIds.DispatchEventFailedToParseEventDescriptor,
"Failed to parse the event descriptor data when trying to dispatch an event.");
EventIds.DispatchEventFailedToParseEventData,
"Failed to parse the event data when trying to dispatch an event.");
_dispatchEventFailedToDispatchEvent = LoggerMessage.Define<string>(
LogLevel.Debug,
EventIds.DispatchEventFailedToDispatchEvent,
"There was an error dispatching the event '{EventHandlerId}' to the application.");
_dispatchEventThroughJSInterop = LoggerMessage.Define(
LogLevel.Debug,
EventIds.DispatchEventThroughJSInterop,
"There was an intent to dispatch a browser event through JS interop.");
_locationChange = LoggerMessage.Define<string, string>(
LogLevel.Debug,
EventIds.LocationChange,
@ -555,7 +530,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
public static void EndInvokeJSSucceeded(ILogger logger, long asyncCall) => _endInvokeJSSucceeded(logger, asyncCall, null);
public static void DispatchEventFailedToParseEventDescriptor(ILogger logger, Exception ex) => _dispatchEventFailedToParseEventDescriptor(logger, ex);
public static void DispatchEventFailedToParseEventData(ILogger logger, Exception ex) => _dispatchEventFailedToParseEventData(logger, ex);
public static void DispatchEventFailedToDispatchEvent(ILogger logger, string eventHandlerId, Exception ex) => _dispatchEventFailedToDispatchEvent(logger, eventHandlerId ?? "", ex);
@ -571,8 +546,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
}
}
public static void DispatchEventTroughJSInterop(ILogger logger) => _dispatchEventThroughJSInterop(logger, null);
public static void LocationChange(ILogger logger, string circuitId, string uri) => _locationChange(logger, circuitId, uri, null);
public static void LocationChangeSucceeded(ILogger logger, string circuitId, string uri) => _locationChangeSucceeded(logger, circuitId, uri, null);

View File

@ -72,11 +72,9 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
navigationManager.Initialize(baseUri, uri);
}
var rendererRegistry = new RendererRegistry();
var renderer = new RemoteRenderer(
scope.ServiceProvider,
_loggerFactory,
rendererRegistry,
_options,
jsRuntime,
client,
@ -91,7 +89,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
_circuitIdFactory.CreateCircuitId(),
scope,
client,
rendererRegistry,
renderer,
components,
jsRuntime,

View File

@ -24,7 +24,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
private readonly IJSRuntime _jsRuntime;
private readonly CircuitClientProxy _client;
private readonly CircuitOptions _options;
private readonly RendererRegistry _rendererRegistry;
private readonly ILogger _logger;
internal readonly ConcurrentQueue<UnacknowledgedRenderBatch> _unacknowledgedRenderBatches = new ConcurrentQueue<UnacknowledgedRenderBatch>();
private long _nextRenderId = 1;
@ -41,7 +40,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
public RemoteRenderer(
IServiceProvider serviceProvider,
ILoggerFactory loggerFactory,
RendererRegistry rendererRegistry,
CircuitOptions options,
IJSRuntime jsRuntime,
CircuitClientProxy client,
@ -49,19 +47,14 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
ILogger logger)
: base(serviceProvider, loggerFactory, encoder.Encode)
{
_rendererRegistry = rendererRegistry;
_jsRuntime = jsRuntime;
_client = client;
_options = options;
Id = _rendererRegistry.Add(this);
_logger = logger;
}
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
public int Id { get; }
/// <summary>
/// Associates the <see cref="IComponent"/> with the <see cref="RemoteRenderer"/>,
/// causing it to be displayed in the specified DOM element.
@ -75,7 +68,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
var attachComponentTask = _jsRuntime.InvokeAsync<object>(
"Blazor._internal.attachRootComponentToElement",
Id,
domElementSelector,
componentId);
CaptureAsyncExceptions(attachComponentTask);
@ -133,7 +125,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
protected override void Dispose(bool disposing)
{
_disposing = true;
_rendererRegistry.TryRemove(Id);
while (_unacknowledgedRenderBatches.TryDequeue(out var entry))
{
entry.CompletionSource.TrySetCanceled();
@ -220,7 +211,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
Log.BeginUpdateDisplayAsync(_logger, _client.ConnectionId, pending.BatchId, pending.Data.Count);
var segment = new ArraySegment<byte>(pending.Data.Buffer, 0, pending.Data.Count);
await _client.SendAsync("JS.RenderBatch", Id, pending.BatchId, segment);
await _client.SendAsync("JS.RenderBatch", pending.BatchId, segment);
}
catch (Exception e)
{

View File

@ -41,6 +41,10 @@
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
<Compile Include="..\..\Shared\src\BrowserNavigationManagerInterop.cs" />
<Compile Include="..\..\Shared\src\JsonSerializerOptionsProvider.cs" />
<Compile Include="..\..\Shared\src\WebEventData.cs" />
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\BinaryMessageFormatter.cs" LinkBase="BlazorPack" />
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\BinaryMessageParser.cs" LinkBase="BlazorPack" />
<Compile Include="$(RepoRoot)src\SignalR\common\Shared\MemoryBufferWriter.cs" LinkBase="BlazorPack" />

View File

@ -232,15 +232,14 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
return new TestRemoteRenderer(
Mock.Of<IServiceProvider>(),
new RendererRegistry(),
Mock.Of<IJSRuntime>(),
Mock.Of<IClientProxy>());
}
private class TestRemoteRenderer : RemoteRenderer
{
public TestRemoteRenderer(IServiceProvider serviceProvider, RendererRegistry rendererRegistry, IJSRuntime jsRuntime, IClientProxy client)
: base(serviceProvider, NullLoggerFactory.Instance, rendererRegistry, new CircuitOptions(), jsRuntime, new CircuitClientProxy(client, "connection"), HtmlEncoder.Default, NullLogger.Instance)
public TestRemoteRenderer(IServiceProvider serviceProvider, IJSRuntime jsRuntime, IClientProxy client)
: base(serviceProvider, NullLoggerFactory.Instance, new CircuitOptions(), jsRuntime, new CircuitClientProxy(client, "connection"), HtmlEncoder.Default, NullLogger.Instance)
{
}

View File

@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
var initialClient = new Mock<IClientProxy>();
initialClient.Setup(c => c.SendCoreAsync(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()))
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[1]))
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[0]))
.Returns(firstBatchTCS.Task);
var circuitClient = new CircuitClientProxy(initialClient.Object, "connection0");
var renderer = GetRemoteRenderer(serviceProvider, circuitClient);
@ -155,8 +155,8 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
var client = new Mock<IClientProxy>();
client.Setup(c => c.SendCoreAsync(It.IsAny<string>(), It.IsAny<object[]>(), It.IsAny<CancellationToken>()))
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[1]))
.Returns<string, object[], CancellationToken>((n, v, t) => (long)v[1] == 3 ? secondBatchTCS.Task : thirdBatchTCS.Task);
.Callback((string name, object[] value, CancellationToken token) => renderIds.Add((long)value[0]))
.Returns<string, object[], CancellationToken>((n, v, t) => (long)v[0] == 3 ? secondBatchTCS.Task : thirdBatchTCS.Task);
var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender();
@ -465,7 +465,6 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
return new RemoteRenderer(
serviceProvider,
NullLoggerFactory.Instance,
new RendererRegistry(),
new CircuitOptions(),
jsRuntime.Object,
circuitClientProxy,

View File

@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
internal class TestCircuitHost : CircuitHost
{
private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RendererRegistry rendererRegistry, RemoteRenderer renderer, IReadOnlyList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
: base(circuitId, scope, client, rendererRegistry, renderer, descriptors, jsRuntime, circuitHandlers, logger)
private TestCircuitHost(string circuitId, IServiceScope scope, CircuitClientProxy client, RemoteRenderer renderer, IReadOnlyList<ComponentDescriptor> descriptors, RemoteJSRuntime jsRuntime, CircuitHandler[] circuitHandlers, ILogger logger)
: base(circuitId, scope, client, renderer, descriptors, jsRuntime, circuitHandlers, logger)
{
}
@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
serviceScope = serviceScope ?? Mock.Of<IServiceScope>();
clientProxy = clientProxy ?? new CircuitClientProxy(Mock.Of<IClientProxy>(), Guid.NewGuid().ToString());
var renderRegistry = new RendererRegistry();
var jsRuntime = new RemoteJSRuntime(Options.Create(new CircuitOptions()), Mock.Of<ILogger<RemoteJSRuntime>>());
if (remoteRenderer == null)
@ -45,7 +44,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
remoteRenderer = new RemoteRenderer(
serviceScope.ServiceProvider ?? Mock.Of<IServiceProvider>(),
NullLoggerFactory.Instance,
new RendererRegistry(),
new CircuitOptions(),
jsRuntime,
clientProxy,
@ -58,7 +56,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
circuitId ?? Guid.NewGuid().ToString(),
serviceScope,
clientProxy,
renderRegistry,
remoteRenderer,
new List<ComponentDescriptor>(),
jsRuntime,

View File

@ -3,57 +3,47 @@
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;
namespace Microsoft.AspNetCore.Components.Web
{
/// <summary>
/// Provides mechanisms for dispatching events to components in a <see cref="Renderer"/>.
/// </summary>
public static class RendererRegistryEventDispatcher
internal class WebEventData
{
/// <summary>
/// For framework use only.
/// </summary>
[JSInvokable(nameof(DispatchEvent))]
public static Task DispatchEvent(BrowserEventDescriptor eventDescriptor, string eventArgsJson)
// This class represents the second half of parsing incoming event data,
// once the type of the eventArgs becomes known.
public static WebEventData Parse(string eventDescriptorJson, string eventArgsJson)
{
InterpretEventDescriptor(eventDescriptor);
var eventArgs = ParseEventArgsJson(eventDescriptor.EventArgsType, eventArgsJson);
var renderer = RendererRegistry.Current.Find(eventDescriptor.BrowserRendererId);
return renderer.DispatchEventAsync(eventDescriptor.EventHandlerId, eventDescriptor.EventFieldInfo, eventArgs);
return Parse(
Deserialize<WebEventDescriptor>(eventDescriptorJson),
eventArgsJson);
}
private static void InterpretEventDescriptor(BrowserEventDescriptor eventDescriptor)
public static WebEventData Parse(WebEventDescriptor eventDescriptor, string eventArgsJson)
{
// The incoming field value can be either a bool or a string, but since the .NET property
// type is 'object', it will deserialize initially as a JsonElement
var fieldInfo = eventDescriptor.EventFieldInfo;
if (fieldInfo != null)
{
if (fieldInfo.FieldValue is JsonElement attributeValueJsonElement)
{
switch (attributeValueJsonElement.ValueKind)
{
case JsonValueKind.True:
case JsonValueKind.False:
fieldInfo.FieldValue = attributeValueJsonElement.GetBoolean();
break;
default:
fieldInfo.FieldValue = attributeValueJsonElement.GetString();
break;
}
}
else
{
// Unanticipated value type. Ensure we don't do anything with it.
eventDescriptor.EventFieldInfo = null;
}
}
return new WebEventData(
eventDescriptor.BrowserRendererId,
eventDescriptor.EventHandlerId,
InterpretEventFieldInfo(eventDescriptor.EventFieldInfo),
ParseEventArgsJson(eventDescriptor.EventArgsType, eventArgsJson));
}
private WebEventData(int browserRendererId, ulong eventHandlerId, EventFieldInfo eventFieldInfo, EventArgs eventArgs)
{
BrowserRendererId = browserRendererId;
EventHandlerId = eventHandlerId;
EventFieldInfo = eventFieldInfo;
EventArgs = eventArgs;
}
public int BrowserRendererId { get; }
public ulong EventHandlerId { get; }
public EventFieldInfo EventFieldInfo { get; }
public EventArgs EventArgs { get; }
private static EventArgs ParseEventArgsJson(string eventArgsType, string eventArgsJson)
{
switch (eventArgsType)
@ -83,13 +73,40 @@ namespace Microsoft.AspNetCore.Components.Web
case "wheel":
return Deserialize<UIWheelEventArgs>(eventArgsJson);
default:
throw new ArgumentException($"Unsupported value '{eventArgsType}'.", nameof(eventArgsType));
throw new ArgumentException($"Unsupported value '{eventArgsType}'.", nameof(eventArgsType));
}
}
private static T Deserialize<T>(string eventArgsJson)
private static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(eventArgsJson, JsonSerializerOptionsProvider.Options);
return JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options);
}
private static EventFieldInfo InterpretEventFieldInfo(EventFieldInfo fieldInfo)
{
// The incoming field value can be either a bool or a string, but since the .NET property
// type is 'object', it will deserialize initially as a JsonElement
if (fieldInfo?.FieldValue is JsonElement attributeValueJsonElement)
{
switch (attributeValueJsonElement.ValueKind)
{
case JsonValueKind.True:
case JsonValueKind.False:
return new EventFieldInfo
{
ComponentId = fieldInfo.ComponentId,
FieldValue = attributeValueJsonElement.GetBoolean()
};
default:
return new EventFieldInfo
{
ComponentId = fieldInfo.ComponentId,
FieldValue = attributeValueJsonElement.GetString()
};
}
}
return null;
}
private static ChangeEventArgs DeserializeChangeEventArgs(string eventArgsJson)
@ -113,31 +130,5 @@ namespace Microsoft.AspNetCore.Components.Web
}
return changeArgs;
}
/// <summary>
/// For framework use only.
/// </summary>
public class BrowserEventDescriptor
{
/// <summary>
/// For framework use only.
/// </summary>
public int BrowserRendererId { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public ulong EventHandlerId { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public string EventArgsType { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public EventFieldInfo EventFieldInfo { get; set; }
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -85,12 +85,11 @@ async function initializeConnection(options: BlazorOptions, logger: Logger): Pro
connection.on('JS.BeginInvokeJS', DotNet.jsCallDispatcher.beginInvokeJSFromDotNet);
connection.on('JS.EndInvokeDotNet', (args: string) => DotNet.jsCallDispatcher.endInvokeDotNetFromJS(...(JSON.parse(args) as [string, boolean, unknown])));
connection.on('JS.RenderBatch', (browserRendererId: number, batchId: number, batchData: Uint8Array) => {
logger.log(LogLevel.Debug, `Received render batch for ${browserRendererId} with id ${batchId} and ${batchData.byteLength} bytes.`);
const queue = RenderQueue.getOrCreateQueue(browserRendererId, logger);
queue.processBatch(batchId, batchData, connection);
const renderQueue = new RenderQueue(/* renderer ID unused with remote renderer */ 0, logger);
connection.on('JS.RenderBatch', (batchId: number, batchData: Uint8Array) => {
logger.log(LogLevel.Debug, `Received render batch with id ${batchId} and ${batchData.byteLength} bytes.`);
renderQueue.processBatch(batchId, batchData, connection);
});
connection.onclose(error => !renderingFailed && options.reconnectionHandler!.onConnectionDown(options.reconnectionOptions, error));

View File

@ -18,7 +18,7 @@ async function boot(options?: any): Promise<void> {
}
started = true;
setEventDispatcher((eventDescriptor, eventArgs) => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Components.Web', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
setEventDispatcher((eventDescriptor, eventArgs) => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Blazor', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
// Configure environment for execution under Mono WebAssembly with shared-memory rendering
const platform = Environment.setPlatform(monoPlatform);

View File

@ -4,8 +4,6 @@ import { Logger, LogLevel } from '../Logging/Logger';
import { HubConnection } from '@aspnet/signalr';
export class RenderQueue {
private static renderQueues = new Map<number, RenderQueue>();
private nextBatchId = 2;
private fatalError?: string;
@ -19,17 +17,6 @@ export class RenderQueue {
this.logger = logger;
}
public static getOrCreateQueue(browserRendererId: number, logger: Logger): RenderQueue {
const queue = this.renderQueues.get(browserRendererId);
if (queue) {
return queue;
}
const newQueue = new RenderQueue(browserRendererId, logger);
this.renderQueues.set(browserRendererId, newQueue);
return newQueue;
}
public async processBatch(receivedBatchId: number, batchData: Uint8Array, connection: HubConnection): Promise<void> {
if (receivedBatchId < this.nextBatchId) {
// SignalR delivers messages in order, but it does not guarantee that the message gets delivered.

View File

@ -12,7 +12,6 @@ const browserRenderers: BrowserRendererRegistry = {};
let shouldResetScrollAfterNextBatch = false;
export function attachRootComponentToLogicalElement(browserRendererId: number, logicalElement: LogicalElement, componentId: number): void {
let browserRenderer = browserRenderers[browserRendererId];
if (!browserRenderer) {
browserRenderer = browserRenderers[browserRendererId] = new BrowserRenderer(browserRendererId);
@ -21,15 +20,15 @@ export function attachRootComponentToLogicalElement(browserRendererId: number, l
browserRenderer.attachRootComponentToLogicalElement(componentId, logicalElement);
}
export function attachRootComponentToElement(browserRendererId: number, elementSelector: string, componentId: number): void {
export function attachRootComponentToElement(elementSelector: string, componentId: number, browserRendererId?: number): void {
const element = document.querySelector(elementSelector);
if (!element) {
throw new Error(`Could not find any element matching selector '${elementSelector}'.`);
}
// 'allowExistingContents' to keep any prerendered content until we do the first client-side render
attachRootComponentToLogicalElement(browserRendererId, toLogicalElement(element, /* allow existing contents */ true), componentId);
// Only client-side Blazor supplies a browser renderer ID
attachRootComponentToLogicalElement(browserRendererId || 0, toLogicalElement(element, /* allow existing contents */ true), componentId);
}
export function renderBatch(browserRendererId: number, batch: RenderBatch): void {

View File

@ -10,23 +10,8 @@ jest.mock('../src/Rendering/Renderer', () => ({
describe('RenderQueue', () => {
it('getOrCreateRenderQueue returns a new queue if one does not exist for a renderer', () => {
const queue = RenderQueue.getOrCreateQueue(1, NullLogger.instance);
expect(queue).toBeDefined();
});
it('getOrCreateRenderQueue returns an existing queue if one exists for a renderer', () => {
const queue = RenderQueue.getOrCreateQueue(2, NullLogger.instance);
const secondQueue = RenderQueue.getOrCreateQueue(2, NullLogger.instance);
expect(secondQueue).toBe(queue);
});
it('processBatch acknowledges previously rendered batches', () => {
const queue = RenderQueue.getOrCreateQueue(3, NullLogger.instance);
const queue = new RenderQueue(0, NullLogger.instance);
const sendMock = jest.fn();
const connection = { send: sendMock } as any as signalR.HubConnection;
@ -37,7 +22,7 @@ describe('RenderQueue', () => {
});
it('processBatch does not render out of order batches', () => {
const queue = RenderQueue.getOrCreateQueue(4, NullLogger.instance);
const queue = new RenderQueue(0, NullLogger.instance);
const sendMock = jest.fn();
const connection = { send: sendMock } as any as signalR.HubConnection;
@ -47,7 +32,7 @@ describe('RenderQueue', () => {
});
it('processBatch renders pending batches', () => {
const queue = RenderQueue.getOrCreateQueue(5, NullLogger.instance);
const queue = new RenderQueue(0, NullLogger.instance);
const sendMock = jest.fn();
const connection = { send: sendMock } as any as signalR.HubConnection;

View File

@ -308,25 +308,25 @@ namespace Microsoft.AspNetCore.Components.Forms
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override void OnParametersSet() { }
}
public abstract partial class InputBase<T> : Microsoft.AspNetCore.Components.ComponentBase
public abstract partial class InputBase<TValue> : Microsoft.AspNetCore.Components.ComponentBase
{
protected InputBase() { }
[Microsoft.AspNetCore.Components.ParameterAttribute(CaptureUnmatchedValues=true)]
public System.Collections.Generic.IReadOnlyDictionary<string, object> AdditionalAttributes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected string CssClass { get { throw null; } }
protected T CurrentValue { get { throw null; } set { } }
protected TValue CurrentValue { get { throw null; } set { } }
protected string CurrentValueAsString { get { throw null; } set { } }
protected Microsoft.AspNetCore.Components.Forms.EditContext EditContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public T Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.EventCallback<T> ValueChanged { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public Microsoft.AspNetCore.Components.EventCallback<TValue> ValueChanged { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Linq.Expressions.Expression<System.Func<T>> ValueExpression { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected virtual string FormatValueAsString(T value) { throw null; }
public System.Linq.Expressions.Expression<System.Func<TValue>> ValueExpression { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected virtual string FormatValueAsString(TValue value) { throw null; }
public override System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
protected abstract bool TryParseValueFromString(string value, out T result, out string validationErrorMessage);
protected abstract bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage);
}
public partial class InputCheckbox : Microsoft.AspNetCore.Components.Forms.InputBase<bool>
{
@ -334,31 +334,31 @@ namespace Microsoft.AspNetCore.Components.Forms
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override bool TryParseValueFromString(string value, out bool result, out string validationErrorMessage) { throw null; }
}
public partial class InputDate<T> : Microsoft.AspNetCore.Components.Forms.InputBase<T>
public partial class InputDate<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputDate() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string ParsingErrorMessage { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override string FormatValueAsString(T value) { throw null; }
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage) { throw null; }
protected override string FormatValueAsString(TValue value) { throw null; }
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage) { throw null; }
}
public partial class InputNumber<T> : Microsoft.AspNetCore.Components.Forms.InputBase<T>
public partial class InputNumber<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputNumber() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string ParsingErrorMessage { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override string FormatValueAsString(T value) { throw null; }
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage) { throw null; }
protected override string FormatValueAsString(TValue value) { throw null; }
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage) { throw null; }
}
public partial class InputSelect<T> : Microsoft.AspNetCore.Components.Forms.InputBase<T>
public partial class InputSelect<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputSelect() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage) { throw null; }
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage) { throw null; }
}
public partial class InputText : Microsoft.AspNetCore.Components.Forms.InputBase<string>
{
@ -372,13 +372,13 @@ namespace Microsoft.AspNetCore.Components.Forms
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage) { throw null; }
}
public partial class ValidationMessage<T> : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
public partial class ValidationMessage<TValue> : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
{
public ValidationMessage() { }
[Microsoft.AspNetCore.Components.ParameterAttribute(CaptureUnmatchedValues=true)]
public System.Collections.Generic.IReadOnlyDictionary<string, object> AdditionalAttributes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Linq.Expressions.Expression<System.Func<T>> For { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Linq.Expressions.Expression<System.Func<TValue>> For { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected virtual void Dispose(bool disposing) { }
protected override void OnParametersSet() { }
@ -422,17 +422,12 @@ namespace Microsoft.AspNetCore.Components.Routing
}
namespace Microsoft.AspNetCore.Components.Web
{
public static partial class RendererRegistryEventDispatcher
public sealed partial class WebEventDescriptor
{
[Microsoft.JSInterop.JSInvokableAttribute("DispatchEvent")]
public static System.Threading.Tasks.Task DispatchEvent(Microsoft.AspNetCore.Components.Web.RendererRegistryEventDispatcher.BrowserEventDescriptor eventDescriptor, string eventArgsJson) { throw null; }
public partial class BrowserEventDescriptor
{
public BrowserEventDescriptor() { }
public int BrowserRendererId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public Microsoft.AspNetCore.Components.Rendering.EventFieldInfo EventFieldInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public ulong EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
public WebEventDescriptor() { }
public int BrowserRendererId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string EventArgsType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public Microsoft.AspNetCore.Components.Rendering.EventFieldInfo EventFieldInfo { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public ulong EventHandlerId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
}

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.Forms
/// integrates with an <see cref="Forms.EditContext"/>, which must be supplied
/// as a cascading parameter.
/// </summary>
public abstract class InputBase<T> : ComponentBase
public abstract class InputBase<TValue> : ComponentBase
{
private bool _previousParsingAttemptFailed;
private ValidationMessageStore _parsingValidationMessages;
@ -32,20 +32,20 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <example>
/// @bind-Value="model.PropertyName"
/// </example>
[Parameter] public T Value { get; set; }
[Parameter] public TValue Value { get; set; }
/// <summary>
/// Gets or sets a callback that updates the bound value.
/// </summary>
[Parameter] public EventCallback<T> ValueChanged { get; set; }
[Parameter] public EventCallback<TValue> ValueChanged { get; set; }
/// <summary>
/// Gets or sets an expression that identifies the bound value.
/// </summary>
[Parameter] public Expression<Func<T>> ValueExpression { get; set; }
[Parameter] public Expression<Func<TValue>> ValueExpression { get; set; }
/// <summary>
/// Gets the associated <see cref="Microsoft.AspNetCore.Components.Forms.EditContext"/>.
/// Gets the associated <see cref="Forms.EditContext"/>.
/// </summary>
protected EditContext EditContext { get; set; }
@ -57,12 +57,12 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <summary>
/// Gets or sets the current value of the input.
/// </summary>
protected T CurrentValue
protected TValue CurrentValue
{
get => Value;
set
{
var hasChanged = !EqualityComparer<T>.Default.Equals(value, Value);
var hasChanged = !EqualityComparer<TValue>.Default.Equals(value, Value);
if (hasChanged)
{
Value = value;
@ -126,18 +126,18 @@ namespace Microsoft.AspNetCore.Components.Forms
/// </summary>
/// <param name="value">The value to format.</param>
/// <returns>A string representation of the value.</returns>
protected virtual string FormatValueAsString(T value)
protected virtual string FormatValueAsString(TValue value)
=> value?.ToString();
/// <summary>
/// Parses a string to create an instance of <typeparamref name="T"/>. Derived classes can override this to change how
/// Parses a string to create an instance of <typeparamref name="TValue"/>. Derived classes can override this to change how
/// <see cref="CurrentValueAsString"/> interprets incoming values.
/// </summary>
/// <param name="value">The string value to be parsed.</param>
/// <param name="result">An instance of <typeparamref name="T"/>.</param>
/// <param name="result">An instance of <typeparamref name="TValue"/>.</param>
/// <param name="validationErrorMessage">If the value could not be parsed, provides a validation error message.</param>
/// <returns>True if the value could be parsed; otherwise false.</returns>
protected abstract bool TryParseValueFromString(string value, out T result, out string validationErrorMessage);
protected abstract bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage);
/// <summary>
/// Gets a string that indicates the status of the field being edited. This will include
@ -192,7 +192,7 @@ namespace Microsoft.AspNetCore.Components.Forms
EditContext = CascadedEditContext;
FieldIdentifier = FieldIdentifier.Create(ValueExpression);
_nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
_nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(TValue));
}
else if (CascadedEditContext != EditContext)
{

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Components.Forms
/// An input component for editing date values.
/// Supported types are <see cref="DateTime"/> and <see cref="DateTimeOffset"/>.
/// </summary>
public class InputDate<T> : InputBase<T>
public class InputDate<TValue> : InputBase<TValue>
{
private const string DateFormat = "yyyy-MM-dd"; // Compatible with HTML date inputs
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Components.Forms
}
/// <inheritdoc />
protected override string FormatValueAsString(T value)
protected override string FormatValueAsString(TValue value)
{
switch (value)
{
@ -47,11 +47,11 @@ namespace Microsoft.AspNetCore.Components.Forms
}
/// <inheritdoc />
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
// Unwrap nullable types. We don't have to deal with receiving empty values for nullable
// types here, because the underlying InputBase already covers that.
var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
var targetType = Nullable.GetUnderlyingType(typeof(TValue)) ?? typeof(TValue);
bool success;
if (targetType == typeof(DateTime))
@ -79,12 +79,12 @@ namespace Microsoft.AspNetCore.Components.Forms
}
}
static bool TryParseDateTime(string value, out T result)
static bool TryParseDateTime(string value, out TValue result)
{
var success = BindConverter.TryConvertToDateTime(value, CultureInfo.InvariantCulture, DateFormat, out var parsedValue);
if (success)
{
result = (T)(object)parsedValue;
result = (TValue)(object)parsedValue;
return true;
}
else
@ -94,12 +94,12 @@ namespace Microsoft.AspNetCore.Components.Forms
}
}
static bool TryParseDateTimeOffset(string value, out T result)
static bool TryParseDateTimeOffset(string value, out TValue result)
{
var success = BindConverter.TryConvertToDateTimeOffset(value, CultureInfo.InvariantCulture, DateFormat, out var parsedValue);
if (success)
{
result = (T)(object)parsedValue;
result = (TValue)(object)parsedValue;
return true;
}
else

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Components.Forms
/// An input component for editing numeric values.
/// Supported numeric types are <see cref="int"/>, <see cref="long"/>, <see cref="float"/>, <see cref="double"/>, <see cref="decimal"/>.
/// </summary>
public class InputNumber<T> : InputBase<T>
public class InputNumber<TValue> : InputBase<TValue>
{
private static string _stepAttributeValue; // Null by default, so only allows whole numbers as per HTML spec
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Components.Forms
{
// Unwrap Nullable<T>, because InputBase already deals with the Nullable aspect
// of it for us. We will only get asked to parse the T for nonempty inputs.
var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
var targetType = Nullable.GetUnderlyingType(typeof(TValue)) ?? typeof(TValue);
if (targetType == typeof(int) ||
targetType == typeof(float) ||
targetType == typeof(double) ||
@ -52,9 +52,9 @@ namespace Microsoft.AspNetCore.Components.Forms
}
/// <inheritdoc />
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
if (BindConverter.TryConvertTo<T>(value, CultureInfo.InvariantCulture, out result))
if (BindConverter.TryConvertTo<TValue>(value, CultureInfo.InvariantCulture, out result))
{
validationErrorMessage = null;
return true;
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components.Forms
/// </summary>
/// <param name="value">The value to format.</param>
/// <returns>A string representation of the value.</returns>
protected override string FormatValueAsString(T value)
protected override string FormatValueAsString(TValue value)
{
// Avoiding a cast to IFormattable to avoid boxing.
switch (value)

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <summary>
/// A dropdown selection component.
/// </summary>
public class InputSelect<T> : InputBase<T>
public class InputSelect<TValue> : InputBase<TValue>
{
/// <summary>
/// Gets or sets the child content to be rendering inside the select element.
@ -30,17 +30,17 @@ namespace Microsoft.AspNetCore.Components.Forms
}
/// <inheritdoc />
protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage)
protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
if (typeof(T) == typeof(string))
if (typeof(TValue) == typeof(string))
{
result = (T)(object)value;
result = (TValue)(object)value;
validationErrorMessage = null;
return true;
}
else if (typeof(T).IsEnum)
else if (typeof(TValue).IsEnum)
{
var success = BindConverter.TryConvertTo<T>(value, CultureInfo.CurrentCulture, out var parsedValue);
var success = BindConverter.TryConvertTo<TValue>(value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
{
result = parsedValue;
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Components.Forms
}
}
throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(T)}'.");
throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(TValue)}'.");
}
}
}

View File

@ -11,10 +11,10 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <summary>
/// Displays a list of validation messages for a specified field within a cascaded <see cref="EditContext"/>.
/// </summary>
public class ValidationMessage<T> : ComponentBase, IDisposable
public class ValidationMessage<TValue> : ComponentBase, IDisposable
{
private EditContext _previousEditContext;
private Expression<Func<T>> _previousFieldAccessor;
private Expression<Func<TValue>> _previousFieldAccessor;
private readonly EventHandler<ValidationStateChangedEventArgs> _validationStateChangedHandler;
private FieldIdentifier _fieldIdentifier;
@ -28,10 +28,10 @@ namespace Microsoft.AspNetCore.Components.Forms
/// <summary>
/// Specifies the field for which validation messages should be displayed.
/// </summary>
[Parameter] public Expression<Func<T>> For { get; set; }
[Parameter] public Expression<Func<TValue>> For { get; set; }
/// <summary>`
/// Constructs an instance of <see cref="ValidationMessage{T}"/>.
/// Constructs an instance of <see cref="ValidationMessage{TValue}"/>.
/// </summary>
public ValidationMessage()
{

View File

@ -1,6 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Server.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -1,95 +0,0 @@
// 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 Microsoft.AspNetCore.Components.Rendering;
using System.Collections.Generic;
using System.Threading;
namespace Microsoft.AspNetCore.Components.Web
{
// Provides mechanisms for locating <see cref="Renderer"/> instances
// by ID. This is used when receiving incoming events. It also implicitly
// roots <see cref="Renderer"/> instances and their associated component instances
// so they cannot be GCed while they are still registered for events.
/// <summary>
/// Framework infrastructure, not intended to be used by application code.
/// </summary>
internal class RendererRegistry
{
private static AsyncLocal<RendererRegistry> _current;
private static readonly RendererRegistry _globalRegistry;
// By default the registry will be set to a default value. This means that
// things will 'just work when running in the browser.
//
// Running in Server-Side Components - any call into the Circuit will set this value via
// the async local. This will ensure that the incoming call can resolve the correct
// renderer associated with the user context.
static RendererRegistry()
{
_current = new AsyncLocal<RendererRegistry>();
_globalRegistry = new RendererRegistry();
}
/// <summary>
/// Framework infrastructure, not intended to be used by application code.
/// </summary>
public static RendererRegistry Current => _current.Value ?? _globalRegistry;
/// <summary>
/// Framework infrastructure, not intended by used by application code.
/// </summary>
public static void SetCurrentRendererRegistry(RendererRegistry registry)
{
_current.Value = registry;
}
private int _nextId;
private IDictionary<int, Renderer> _renderers = new Dictionary<int, Renderer>();
/// <summary>
/// Framework infrastructure, not intended by used by application code.
/// </summary>
public int Add(Renderer renderer)
{
lock (_renderers)
{
var id = _nextId++;
_renderers.Add(id, renderer);
return id;
}
}
/// <summary>
/// Framework infrastructure, not intended by used by application code.
/// </summary>
public Renderer Find(int rendererId)
{
lock (_renderers)
{
return _renderers[rendererId];
}
}
/// <summary>
/// Framework infrastructure, not intended by used by application code.
/// </summary>
public bool TryRemove(int rendererId)
{
lock (_renderers)
{
if (_renderers.ContainsKey(rendererId))
{
_renderers.Remove(rendererId);
return true;
}
else
{
return false;
}
}
}
}
}

View File

@ -0,0 +1,38 @@
// 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 Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components.Web
{
/// <summary>
/// For framework use only.
/// </summary>
public sealed class WebEventDescriptor
{
// We split the incoming event data in two, because we don't know what type
// to use when deserializing the args until we've deserialized the descriptor.
// This class represents the first half of the parsing process.
// It's public only because it's part of the signature of a [JSInvokable] method.
/// <summary>
/// For framework use only.
/// </summary>
public int BrowserRendererId { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public ulong EventHandlerId { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public string EventArgsType { get; set; }
/// <summary>
/// For framework use only.
/// </summary>
public EventFieldInfo EventFieldInfo { get; set; }
}
}

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
private void CreateDefaultConfiguration()
{
Client = new BlazorClient() { DefaultLatencyTimeout = DefaultLatencyTimeout };
Client.RenderBatchReceived += (id, rendererId, data) => Batches.Add(new Batch(id, rendererId, data));
Client.RenderBatchReceived += (id, data) => Batches.Add(new Batch(id, data));
Client.OnCircuitError += (error) => Errors.Add(error);
_ = _serverFixture.RootUri; // this is needed for the side-effects of getting the URI.
@ -267,15 +267,13 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
private class Batch
{
public Batch(int id, int rendererId, byte [] data)
public Batch(int id, byte [] data)
{
Id = id;
RendererId = rendererId;
Data = data;
}
public int Id { get; }
public int RendererId { get; }
public byte[] Data { get; }
}
}

View File

@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
await ValidateClientKeepsWorking(Client, batches);
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12940")]
public async Task CannotInvokeJSInvokableMethodsWithWrongNumberOfArguments()
{
// Arrange
@ -259,7 +259,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
await ValidateClientKeepsWorking(Client, batches);
}
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12962")]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12940")]
public async Task LogsJSInteropCompletionsCallbacksAndContinuesWorkingInAllSituations()
{
// Arrange
@ -386,7 +386,30 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
null);
Assert.Contains(
(LogLevel.Debug, "DispatchEventFailedToParseEventDescriptor"),
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
logEvents);
await ValidateClientKeepsWorking(Client, batches);
}
[Fact]
public async Task DispatchingEventsWithInvalidEventDescriptor()
{
// Arrange
var (interopCalls, dotNetCompletions, batches) = ConfigureClient();
await GoToTestComponent(batches);
var sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
var logEvents = new List<(LogLevel logLevel, string)>();
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
// Act
await Client.HubConnection.InvokeAsync(
"DispatchBrowserEvent",
"{Invalid:{\"payload}",
"{}");
Assert.Contains(
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
logEvents);
await ValidateClientKeepsWorking(Client, batches);
@ -403,7 +426,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
// Act
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
var browserDescriptor = new WebEventDescriptor()
{
BrowserRendererId = 0,
EventHandlerId = 6,
@ -416,13 +439,13 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
"{Invalid:{\"payload}");
Assert.Contains(
(LogLevel.Debug, "DispatchEventFailedToDispatchEvent"),
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
logEvents);
await ValidateClientKeepsWorking(Client, batches);
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12940")]
public async Task DispatchingEventsWithInvalidEventHandlerId()
{
// Arrange
@ -438,7 +461,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
Type = "click",
Detail = 1
};
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
var browserDescriptor = new WebEventDescriptor()
{
BrowserRendererId = 0,
EventHandlerId = 1,
@ -458,47 +481,6 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
await ValidateClientKeepsWorking(Client, batches);
}
[Fact]
public async Task DispatchingEventThroughJSInterop()
{
// Arrange
var (interopCalls, dotNetCompletions, batches) = ConfigureClient();
await GoToTestComponent(batches);
var sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
var logEvents = new List<(LogLevel logLevel, string eventIdName)>();
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
// Act
var mouseEventArgs = new UIMouseEventArgs()
{
Type = "click",
Detail = 1
};
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
{
BrowserRendererId = 0,
EventHandlerId = 1,
EventArgsType = "mouse",
};
var serializerOptions = TestJsonSerializerOptionsProvider.Options;
var uiArgs = JsonSerializer.Serialize(mouseEventArgs, serializerOptions);
await Assert.ThrowsAsync<TaskCanceledException>(() => Client.InvokeDotNetMethod(
0,
"Microsoft.AspNetCore.Components.Web",
"DispatchEvent",
null,
JsonSerializer.Serialize(new object[] { browserDescriptor, uiArgs }, serializerOptions)));
Assert.Contains(
(LogLevel.Debug, "DispatchEventThroughJSInterop"),
logEvents);
await ValidateClientKeepsWorking(Client, batches);
}
[Fact]
public async Task EventHandlerThrowsSyncExceptionTerminatesTheCircuit()
{
@ -519,7 +501,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
"Handler threw an exception" == e.exception.Message);
}
private Task ValidateClientKeepsWorking(BlazorClient Client, List<(int, int, byte[])> batches) =>
private Task ValidateClientKeepsWorking(BlazorClient Client, List<(int, byte[])> batches) =>
ValidateClientKeepsWorking(Client, () => batches.Count);
private async Task ValidateClientKeepsWorking(BlazorClient Client, Func<int> countAccessor)
@ -530,7 +512,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
Assert.Equal(currentBatches + 1, countAccessor());
}
private async Task GoToTestComponent(List<(int, int, byte[])> batches)
private async Task GoToTestComponent(List<(int, byte[])> batches)
{
var rootUri = _serverFixture.RootUri;
Assert.True(await Client.ConnectAsync(new Uri(rootUri, "/subdir"), prerendered: false), "Couldn't connect to the app");
@ -540,12 +522,12 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
Assert.Equal(2, batches.Count);
}
private (List<(int id, string identifier, string args)>, List<string>, List<(int, int, byte[])>) ConfigureClient()
private (List<(int id, string identifier, string args)>, List<string>, List<(int, byte[])>) ConfigureClient()
{
var interopCalls = new List<(int, string, string)>();
Client.JSInterop += (int arg1, string arg2, string arg3) => interopCalls.Add((arg1, arg2, arg3));
var batches = new List<(int, int, byte[])>();
Client.RenderBatchReceived += (id, renderer, data) => batches.Add((id, renderer, data));
var batches = new List<(int, byte[])>();
Client.RenderBatchReceived += (renderer, data) => batches.Add((renderer, data));
var endInvokeDotNetCompletions = new List<string>();
Client.DotNetInteropCompletion += (completion) => endInvokeDotNetCompletions.Add(completion);
return (interopCalls, endInvokeDotNetCompletions, batches);

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
_ = _serverFixture.RootUri;
Client = new BlazorClient() { DefaultLatencyTimeout = DefaultLatencyTimeout };
Client.RenderBatchReceived += (rendererId, id, data) => Batches.Add(new Batch(rendererId, id, data));
Client.RenderBatchReceived += (id, data) => Batches.Add(new Batch(id, data));
Sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
Sink.MessageLogged += LogMessages;
@ -101,15 +101,13 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
private class Batch
{
public Batch(int rendererId, int id, byte[] data)
public Batch(int id, byte[] data)
{
Id = id;
RendererId = rendererId;
Data = data;
}
public int Id { get; }
public int RendererId { get; }
public byte[] Data { get; }
}

View File

@ -50,7 +50,7 @@ namespace Ignitor
public event Action<int, string, string> JSInterop;
public event Action<int, int, byte[]> RenderBatchReceived;
public event Action<int, byte[]> RenderBatchReceived;
public event Action<string> DotNetInteropCompletion;
@ -232,7 +232,7 @@ namespace Ignitor
HubConnection.On<int, string, string>("JS.BeginInvokeJS", OnBeginInvokeJS);
HubConnection.On<string>("JS.EndInvokeDotNet", OnEndInvokeDotNet);
HubConnection.On<int, int, byte[]>("JS.RenderBatch", OnRenderBatch);
HubConnection.On<int, byte[]>("JS.RenderBatch", OnRenderBatch);
HubConnection.On<string>("JS.Error", OnError);
HubConnection.Closed += OnClosedAsync;
@ -286,11 +286,11 @@ namespace Ignitor
}
}
private void OnRenderBatch(int browserRendererId, int batchId, byte[] batchData)
private void OnRenderBatch(int batchId, byte[] batchData)
{
try
{
RenderBatchReceived?.Invoke(browserRendererId, batchId, batchData);
RenderBatchReceived?.Invoke(batchId, batchData);
var batch = RenderBatchReader.Read(batchData);

View File

@ -75,7 +75,7 @@ namespace Ignitor
Value = value
};
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
var webEventDescriptor = new WebEventDescriptor()
{
BrowserRendererId = 0,
EventHandlerId = changeEventDescriptor.EventId,
@ -87,7 +87,7 @@ namespace Ignitor
}
};
return DispatchEventCore(connection, Serialize(browserDescriptor), Serialize(args));
return DispatchEventCore(connection, Serialize(webEventDescriptor), Serialize(args));
}
public Task ClickAsync(HubConnection connection)
@ -102,14 +102,14 @@ namespace Ignitor
Type = clickEventDescriptor.EventName,
Detail = 1
};
var browserDescriptor = new RendererRegistryEventDispatcher.BrowserEventDescriptor()
var webEventDescriptor = new WebEventDescriptor
{
BrowserRendererId = 0,
EventHandlerId = clickEventDescriptor.EventId,
EventArgsType = "mouse",
};
return DispatchEventCore(connection, Serialize(browserDescriptor), Serialize(mouseEventArgs));
return DispatchEventCore(connection, Serialize(webEventDescriptor), Serialize(mouseEventArgs));
}
private static string Serialize<T>(T payload) =>

View File

@ -38,7 +38,7 @@ namespace Ignitor
var done = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
// Click the counter button 1000 times
client.RenderBatchReceived += (int browserRendererId, int batchId, byte[] data) =>
client.RenderBatchReceived += (int batchId, byte[] data) =>
{
if (batchId < 1000)
{

View File

@ -30,6 +30,8 @@
<Content Remove="@(Content)" />
<Content Include="wwwroot\**\*" Pack="true" />
<None Include="build\*" Pack="true" PackagePath="build\" />
<None Include="buildMultiTargeting\*" Pack="true" PackagePath="buildMultiTargeting\" />
<None Include="buildTransitive\*" Pack="true" PackagePath="buildTransitive\" />
<None Include="THIRD-PARTY-NOTICES.txt" Pack="true" PackagePath="/THIRD-PARTY-NOTICES.txt" />
</ItemGroup>

View File

@ -0,0 +1,34 @@
<Project>
<Target Name="ValidateIdentityUIFrameworkVersion" BeforeTargets="Build">
<PropertyGroup>
<_IdentityUIBootstrap3WarningMessage>'Bootstrap3' is obsolete. It is recommended that the application is updated to use a supported Bootstrap version by removing the 'IdentityUIFrameworkVersion' property from '$(MSBuildProjectFile)' or setting 'IdentityUIFrameworkVersion' explicitly to 'Bootstrap4'.
</_IdentityUIBootstrap3WarningMessage>
<_IdentityUIUnknownVersionErrorMessage>The 'IdentityUIFrameworkVersion' '$(IdentityUIFrameworkVersion)' in '$(MSBuildProjectFile)' is not valid. Valid versions are 'Bootstrap3' and 'Bootstrap4'.
</_IdentityUIUnknownVersionErrorMessage>
</PropertyGroup>
<ItemGroup>
<_IdentityUIActiveWarnings Include="IdentityUI001;IdentityUI002" Exclude="$(NoWarn)" />
</ItemGroup>
<Warning Code="IdentityUI001"
Text="$(_IdentityUIBootstrap3WarningMessage)"
File="@(MSBuildThisProjectFile)"
Condition="'$(IdentityUIFrameworkVersion)' == 'Bootstrap3' and '%(_IdentityUIActiveWarnings.Identity)' == 'IdentityUI001' and '$(WarningsAsErrors)' != 'true'" />
<Error Code="IdentityUI001"
Text="$(_IdentityUIBootstrap3WarningMessage)"
File="@(MSBuildThisProjectFile)"
Condition="'$(IdentityUIFrameworkVersion)' == 'Bootstrap3' and '%(_IdentityUIActiveWarnings.Identity)' == 'IdentityUI001' and '$(WarningsAsErrors)' == 'true'" />
<Error
Code="IdentityUI002"
Text="$(_IdentityUIUnknownVersionErrorMessage)"
File="@(MSBuildThisProjectFile)"
Condition="'$(IdentityUIFrameworkVersion)' != 'Bootstrap3' and $(IdentityUIFrameworkVersion) != 'Bootstrap4' and '%(_IdentityUIActiveWarnings.Identity)' == 'IdentityUI002'" />
</Target>
</Project>

View File

@ -0,0 +1,5 @@
<Project>
<Import Project="..\build\Microsoft.AspnetCore.Identity.UI.targets" />
</Project>

View File

@ -0,0 +1,5 @@
<Project>
<Import Project="..\buildMultiTargeting\Microsoft.AspnetCore.Identity.UI.targets" />
</Project>

View File

@ -255,8 +255,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeSe
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SpaServices", "SpaServices", "{D6FA4ABE-E685-4EDD-8B06-D8777E76B472}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Webpack", "SpaServices\samples\Webpack\Webpack.csproj", "{121DFA13-E965-4C91-A175-19EF20DFD5AC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices", "SpaServices\src\Microsoft.AspNetCore.SpaServices.csproj", "{D9D02772-1D53-45C3-B2CC-888F9978958C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{5D5B7E54-9323-498A-8983-E9BDFA3B2D07}"
@ -297,6 +295,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestOrigin", "CORS\test\tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore", "..\DefaultBuilder\src\Microsoft.AspNetCore.csproj", "{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "..\Servers\IIS\IntegrationTesting.IIS\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions.Tests", "SpaServices.Extensions\test\Microsoft.AspNetCore.SpaServices.Extensions.Tests.csproj", "{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1423,18 +1425,6 @@ Global
{40951683-DBC4-437A-BBAB-2FA7147E11EA}.Release|x64.Build.0 = Release|Any CPU
{40951683-DBC4-437A-BBAB-2FA7147E11EA}.Release|x86.ActiveCfg = Release|Any CPU
{40951683-DBC4-437A-BBAB-2FA7147E11EA}.Release|x86.Build.0 = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|x64.ActiveCfg = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|x64.Build.0 = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|x86.ActiveCfg = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Debug|x86.Build.0 = Debug|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|Any CPU.Build.0 = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|x64.ActiveCfg = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|x64.Build.0 = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|x86.ActiveCfg = Release|Any CPU
{121DFA13-E965-4C91-A175-19EF20DFD5AC}.Release|x86.Build.0 = Release|Any CPU
{D9D02772-1D53-45C3-B2CC-888F9978958C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9D02772-1D53-45C3-B2CC-888F9978958C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9D02772-1D53-45C3-B2CC-888F9978958C}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -1627,6 +1617,30 @@ Global
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Release|x64.Build.0 = Release|Any CPU
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Release|x86.ActiveCfg = Release|Any CPU
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Release|x86.Build.0 = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|x64.ActiveCfg = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|x64.Build.0 = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|x86.ActiveCfg = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Debug|x86.Build.0 = Debug|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|Any CPU.Build.0 = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|x64.ActiveCfg = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|x64.Build.0 = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|x86.ActiveCfg = Release|Any CPU
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}.Release|x86.Build.0 = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|x64.ActiveCfg = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|x64.Build.0 = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|x86.ActiveCfg = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Debug|x86.Build.0 = Debug|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|Any CPU.Build.0 = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|x64.ActiveCfg = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|x64.Build.0 = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|x86.ActiveCfg = Release|Any CPU
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1736,7 +1750,6 @@ Global
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{C801B6A3-906F-406F-BA25-EAE0B4BCE5C9} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
{40951683-DBC4-437A-BBAB-2FA7147E11EA} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
{121DFA13-E965-4C91-A175-19EF20DFD5AC} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}
{D9D02772-1D53-45C3-B2CC-888F9978958C} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}
{5D5B7E54-9323-498A-8983-E9BDFA3B2D07} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}
{B04E9CB6-0D1C-4C21-B626-89B6926A491F} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
@ -1755,6 +1768,8 @@ Global
{DFEB537A-2D35-4C62-8A13-42798DF66A80} = {BD7B3AD8-0BA6-405F-8CF6-24B9464D4B5B}
{E0521105-3A7B-480B-B962-0BFC2838D919} = {BD7B3AD8-0BA6-405F-8CF6-24B9464D4B5B}
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA}

View File

@ -3,10 +3,13 @@
namespace Microsoft.AspNetCore.NodeServices
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class EmbeddedResourceReader
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static string Read(System.Type assemblyContainingType, string path) { throw null; }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial interface INodeServices : System.IDisposable
{
System.Threading.Tasks.Task<T> InvokeAsync<T>(string moduleName, params object[] args);
@ -14,10 +17,13 @@ namespace Microsoft.AspNetCore.NodeServices
System.Threading.Tasks.Task<T> InvokeExportAsync<T>(string moduleName, string exportedFunctionName, params object[] args);
System.Threading.Tasks.Task<T> InvokeExportAsync<T>(System.Threading.CancellationToken cancellationToken, string moduleName, string exportedFunctionName, params object[] args);
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class NodeServicesFactory
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static Microsoft.AspNetCore.NodeServices.INodeServices CreateNodeServices(Microsoft.AspNetCore.NodeServices.NodeServicesOptions options) { throw null; }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class NodeServicesOptions
{
public NodeServicesOptions(System.IServiceProvider serviceProvider) { }
@ -31,6 +37,7 @@ namespace Microsoft.AspNetCore.NodeServices
public string ProjectPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string[] WatchFileExtensions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public sealed partial class StringAsTempFile : System.IDisposable
{
public StringAsTempFile(string content, System.Threading.CancellationToken applicationStoppingToken) { }
@ -41,10 +48,12 @@ namespace Microsoft.AspNetCore.NodeServices
}
namespace Microsoft.AspNetCore.NodeServices.HostingModels
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial interface INodeInstance : System.IDisposable
{
System.Threading.Tasks.Task<T> InvokeExportAsync<T>(System.Threading.CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args);
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class NodeInvocationException : System.Exception
{
public NodeInvocationException(string message, string details) { }
@ -52,6 +61,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
public bool AllowConnectionDraining { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public bool NodeInstanceUnavailable { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class NodeInvocationInfo
{
public NodeInvocationInfo() { }
@ -59,10 +69,13 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
public string ExportedFunctionName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string ModuleName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class NodeServicesOptionsExtensions
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void UseHttpHosting(this Microsoft.AspNetCore.NodeServices.NodeServicesOptions options) { }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public abstract partial class OutOfProcessNodeInstance : Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance, System.IDisposable
{
protected readonly Microsoft.Extensions.Logging.ILogger OutputLogger;
@ -80,9 +93,12 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
}
namespace Microsoft.Extensions.DependencyInjection
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class NodeServicesServiceCollectionExtensions
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void AddNodeServices(this Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection) { }
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void AddNodeServices(this Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection, System.Action<Microsoft.AspNetCore.NodeServices.NodeServicesOptions> setupAction) { }
}
}

View File

@ -1,7 +1,9 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.SpaServices.Prerendering;
namespace NodeServicesExamples.Controllers
{
@ -17,7 +19,9 @@ namespace NodeServicesExamples.Controllers
return View();
}
#pragma warning disable 0618
public async Task<IActionResult> Chart([FromServices] INodeServices nodeServices)
#pragma warning restore 0618
{
var options = new { width = 400, height = 200, showArea = true, showPoint = true, fullWidth = true };
var data = new
@ -35,20 +39,6 @@ namespace NodeServicesExamples.Controllers
return View();
}
public async Task<IActionResult> Prerendering([FromServices] ISpaPrerenderer prerenderer)
{
var result = await prerenderer.RenderToString("./Node/prerenderPage");
if (!string.IsNullOrEmpty(result.RedirectUrl))
{
return Redirect(result.RedirectUrl);
}
ViewData["PrerenderedHtml"] = result.Html;
ViewData["PrerenderedGlobals"] = result.CreateGlobalsAssignmentScript();
return View();
}
public IActionResult Error()
{
return View("~/Views/Shared/Error.cshtml");

View File

@ -7,14 +7,15 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.SpaServices" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.Diagnostics" />
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.NodeServices" />
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.Extensions.Logging.Debug" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">

View File

@ -1,15 +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.
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
namespace NodeServicesExamples
{
public class Startup
{
#pragma warning disable 0618
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
@ -17,7 +22,6 @@ namespace NodeServicesExamples
// Enable Node Services
services.AddNodeServices();
services.AddSpaPrerenderer();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -26,11 +30,14 @@ namespace NodeServicesExamples
app.UseDeveloperExceptionPage();
// Dynamically transpile any .js files under the '/js/' directory
app.Use(next => async context => {
app.Use(next => async context =>
{
var requestPath = context.Request.Path.Value;
if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js")) {
if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js"))
{
var fileInfo = env.WebRootFileProvider.GetFileInfo(requestPath);
if (fileInfo.Exists) {
if (fileInfo.Exists)
{
var transpiled = await nodeServices.InvokeAsync<string>("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath);
await context.Response.WriteAsync(transpiled);
return;
@ -49,6 +56,7 @@ namespace NodeServicesExamples
endpoints.MapDefaultControllerRoute();
});
}
#pragma warning restore 0618
public static void Main(string[] args)
{

View File

@ -1,3 +1,6 @@
// 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.AspNetCore.NodeServices
@ -5,6 +8,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// <summary>
/// Supplies INodeServices instances.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static class NodeServicesFactory
{
/// <summary>
@ -12,6 +16,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// </summary>
/// <param name="options">Options for creating the <see cref="INodeServices"/> instance.</param>
/// <returns>An <see cref="INodeServices"/> instance.</returns>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static INodeServices CreateNodeServices(NodeServicesOptions options)
{
if (options == null)
@ -22,4 +27,4 @@ namespace Microsoft.AspNetCore.NodeServices
return new NodeServicesImpl(options.NodeInstanceFactory);
}
}
}
}

View File

@ -1,3 +1,6 @@
// 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.Collections.Generic;
using System.IO;
@ -14,6 +17,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// <summary>
/// Describes options used to configure an <see cref="INodeServices"/> instance.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public class NodeServicesOptions
{
internal const string TimeoutConfigPropertyName = nameof(InvocationTimeoutMilliseconds);

View File

@ -1,3 +1,6 @@
// 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 Microsoft.AspNetCore.NodeServices;
@ -6,12 +9,14 @@ namespace Microsoft.Extensions.DependencyInjection
/// <summary>
/// Extension methods for setting up NodeServices in an <see cref="IServiceCollection" />.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static class NodeServicesServiceCollectionExtensions
{
/// <summary>
/// Adds NodeServices support to the <paramref name="serviceCollection"/>.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void AddNodeServices(this IServiceCollection serviceCollection)
=> AddNodeServices(serviceCollection, _ => {});
@ -20,6 +25,7 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="setupAction">A callback that will be invoked to populate the <see cref="NodeServicesOptions"/>.</param>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void AddNodeServices(this IServiceCollection serviceCollection, Action<NodeServicesOptions> setupAction)
{
if (setupAction == null)

View File

@ -1,3 +1,6 @@
// 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.IO;
using System.Net.Http;
@ -19,6 +22,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// accept RPC invocations.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.NodeServices.HostingModels.OutOfProcessNodeInstance" />
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
internal class HttpNodeInstance : OutOfProcessNodeInstance
{
private static readonly Regex EndpointMessageRegex =

View File

@ -1,3 +1,6 @@
// 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;
@ -7,6 +10,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// <summary>
/// Represents an instance of Node.js to which Remote Procedure Calls (RPC) may be sent.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public interface INodeInstance : IDisposable
{
/// <summary>
@ -20,4 +24,4 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeExportAsync<T>(CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args);
}
}
}

View File

@ -1,3 +1,6 @@
// 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.AspNetCore.NodeServices.HostingModels
@ -5,6 +8,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// <summary>
/// Represents an exception caused by invoking Node.js code.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public class NodeInvocationException : Exception
{
/// <summary>
@ -52,4 +56,4 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
AllowConnectionDraining = allowConnectionDraining;
}
}
}
}

View File

@ -1,8 +1,14 @@
// 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.AspNetCore.NodeServices.HostingModels
{
/// <summary>
/// Describes an RPC call sent from .NET code to Node.js code.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public class NodeInvocationInfo
{
/// <summary>

View File

@ -1,17 +1,24 @@
// 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.AspNetCore.NodeServices.HostingModels
{
/// <summary>
/// Extension methods that help with populating a <see cref="NodeServicesOptions"/> object.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static class NodeServicesOptionsExtensions
{
/// <summary>
/// Configures the <see cref="INodeServices"/> service so that it will use out-of-process
/// Node.js instances and perform RPC calls over HTTP.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void UseHttpHosting(this NodeServicesOptions options)
{
options.NodeInstanceFactory = () => new HttpNodeInstance(options);
}
}
}
}

View File

@ -1,3 +1,6 @@
// 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.Collections.Generic;
using System.Diagnostics;
@ -19,6 +22,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// protocol, or any other RPC-type mechanism).
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance" />
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public abstract class OutOfProcessNodeInstance : INodeInstance
{
/// <summary>
@ -472,4 +476,4 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
Dispose(false);
}
}
}
}

View File

@ -1,3 +1,6 @@
// 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;
@ -9,6 +12,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// might change over time (e.g., the process might be restarted), the <see cref="INodeServices"/> instance
/// will remain constant.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public interface INodeServices : IDisposable
{
/// <summary>
@ -51,4 +55,4 @@ namespace Microsoft.AspNetCore.NodeServices
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeExportAsync<T>(CancellationToken cancellationToken, string moduleName, string exportedFunctionName, params object[] args);
}
}
}

View File

@ -1,3 +1,6 @@
// 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;
@ -17,6 +20,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// analogous to the "connection draining" feature implemented by HTTP load balancers.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.NodeServices.INodeServices" />
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
internal class NodeServicesImpl : INodeServices
{
private static TimeSpan ConnectionDrainingTimespan = TimeSpan.FromSeconds(15);
@ -162,4 +166,4 @@ namespace Microsoft.AspNetCore.NodeServices
return _nodeInstanceFactory();
}
}
}
}

View File

@ -1,3 +1,6 @@
// 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.IO;
using System.Reflection;
@ -7,6 +10,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// <summary>
/// Contains methods for reading embedded resources.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static class EmbeddedResourceReader
{
/// <summary>
@ -15,6 +19,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// <param name="assemblyContainingType">Any <see cref="Type"/> in the assembly whose resource is to be read.</param>
/// <param name="path">The path of the resource to be read.</param>
/// <returns>The contents of the resource.</returns>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static string Read(Type assemblyContainingType, string path)
{
var asm = assemblyContainingType.GetTypeInfo().Assembly;
@ -27,4 +32,4 @@ namespace Microsoft.AspNetCore.NodeServices
}
}
}
}
}

View File

@ -1,3 +1,6 @@
// 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.IO;
using System.Threading;
@ -7,6 +10,7 @@ namespace Microsoft.AspNetCore.NodeServices
/// <summary>
/// Makes it easier to pass script files to Node in a way that's sure to clean up after the process exits.
/// </summary>
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public sealed class StringAsTempFile : IDisposable
{
private bool _disposedValue;
@ -79,4 +83,4 @@ namespace Microsoft.AspNetCore.NodeServices
DisposeImpl(false);
}
}
}
}

View File

@ -1,10 +1,16 @@
// 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.AspNetCore.NodeServices
{
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
internal static class TaskExtensions
{
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static Task OrThrowOnCancellation(this Task task, CancellationToken cancellationToken)
{
return task.IsCompleted
@ -16,6 +22,7 @@ namespace Microsoft.AspNetCore.NodeServices
TaskScheduler.Default);
}
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static Task<T> OrThrowOnCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted
@ -27,4 +34,4 @@ namespace Microsoft.AspNetCore.NodeServices
TaskScheduler.Default);
}
}
}
}

View File

@ -1,15 +1,16 @@
// 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 Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.NodeServices
{
[Obsolete("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public class NodeServicesTest : IDisposable
{
private readonly INodeServices _nodeServices;

View File

@ -7,10 +7,13 @@ namespace Microsoft.AspNetCore.Builder
{
public static void UseSpa(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, System.Action<Microsoft.AspNetCore.SpaServices.ISpaBuilder> configuration) { }
}
[System.ObsoleteAttribute("Prerendering is no longer supported out of box")]
public static partial class SpaPrerenderingExtensions
{
[System.ObsoleteAttribute("Prerendering is no longer supported out of box")]
public static void UseSpaPrerendering(this Microsoft.AspNetCore.SpaServices.ISpaBuilder spaBuilder, System.Action<Microsoft.AspNetCore.Builder.SpaPrerenderingOptions> configuration) { }
}
[System.ObsoleteAttribute("Prerendering is no longer supported out of box")]
public partial class SpaPrerenderingOptions
{
public SpaPrerenderingOptions() { }
@ -45,6 +48,7 @@ namespace Microsoft.AspNetCore.SpaServices
}
namespace Microsoft.AspNetCore.SpaServices.AngularCli
{
[System.ObsoleteAttribute("Prerendering is no longer supported out of box")]
public partial class AngularCliBuilder : Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder
{
public AngularCliBuilder(string npmScript) { }
@ -58,6 +62,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
}
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
[System.ObsoleteAttribute("Prerendering is no longer supported out of box")]
public partial interface ISpaPrerendererBuilder
{
System.Threading.Tasks.Task Build(Microsoft.AspNetCore.SpaServices.ISpaBuilder spaBuilder);

View File

@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
/// Provides an implementation of <see cref="ISpaPrerendererBuilder"/> that can build
/// an Angular application by invoking the Angular CLI.
/// </summary>
[Obsolete("Prerendering is no longer supported out of box")]
public class AngularCliBuilder : ISpaPrerendererBuilder
{
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Helpers for building single-page applications on ASP.NET MVC Core.</Description>
@ -13,4 +13,8 @@
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCode.SpaServices.Extensions.Tests" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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 Microsoft.AspNetCore.Builder;
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.SpaServices.Prerendering
@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
/// so that it can be prerendered. This is only intended to be used at development
/// time. In production, a SPA should already have been built during publishing.
/// </summary>
[Obsolete("Prerendering is no longer supported out of box")]
public interface ISpaPrerendererBuilder
{
/// <summary>

View File

@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Extension methods for configuring prerendering of a Single Page Application.
/// </summary>
[Obsolete("Prerendering is no longer supported out of box")]
public static class SpaPrerenderingExtensions
{
/// <summary>
@ -30,6 +31,7 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
/// <param name="configuration">Supplies configuration for the prerendering middleware.</param>
[Obsolete("Prerendering is no longer supported out of box")]
public static void UseSpaPrerendering(
this ISpaBuilder spaBuilder,
Action<SpaPrerenderingOptions> configuration)

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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 Microsoft.AspNetCore.Http;
@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Represents options for the SPA prerendering middleware.
/// </summary>
[Obsolete("Prerendering is no longer supported out of box")]
public class SpaPrerenderingOptions
{
/// <summary>

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TestDependsOnNode>true</TestDependsOnNode>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.SpaServices.Extensions" />
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.TestHost" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Logging.Testing" />
<Content Include="js\**\*" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.SpaServices.Extensions.Tests
{
public class SpaServicesExtensionsTests
{
[Fact]
public void UseSpa_ThrowsInvalidOperationException_IfRootpathNotSet()
{
// Arrange
var applicationbuilder = GetApplicationBuilder(GetServiceProvider());
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(
() => applicationbuilder.UseSpa(rb => { }));
Assert.Equal("No RootPath was set on the SpaStaticFilesOptions.", exception.Message);
}
private IApplicationBuilder GetApplicationBuilder(IServiceProvider serviceProvider = null)
{
if(serviceProvider == null)
{
serviceProvider = new Mock<IServiceProvider>(MockBehavior.Strict).Object;
}
var applicationbuilderMock = new Mock<IApplicationBuilder>();
applicationbuilderMock
.Setup(s => s.ApplicationServices)
.Returns(serviceProvider);
return applicationbuilderMock.Object;
}
private IServiceProvider GetServiceProvider()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddSpaStaticFiles();
return services.BuildServiceProvider();
}
}
}

View File

@ -3,33 +3,41 @@
namespace Microsoft.AspNetCore.Builder
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class SpaRouteExtensions
{
public static void MapSpaFallbackRoute(this Microsoft.AspNetCore.Routing.IRouteBuilder routeBuilder, string name, object defaults, object constraints = null, object dataTokens = null) { }
public static void MapSpaFallbackRoute(this Microsoft.AspNetCore.Routing.IRouteBuilder routeBuilder, string name, string templatePrefix, object defaults, object constraints = null, object dataTokens = null) { }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class WebpackDevMiddleware
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void UseWebpackDevMiddleware(this Microsoft.AspNetCore.Builder.IApplicationBuilder appBuilder, Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions options = null) { }
}
}
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial interface ISpaPrerenderer
{
System.Threading.Tasks.Task<Microsoft.AspNetCore.SpaServices.Prerendering.RenderToStringResult> RenderToString(string moduleName, string exportName = null, object customDataParameter = null, int timeoutMilliseconds = 0);
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class JavaScriptModuleExport
{
public JavaScriptModuleExport(string moduleName) { }
public string ExportName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string ModuleName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class Prerenderer
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static System.Threading.Tasks.Task<Microsoft.AspNetCore.SpaServices.Prerendering.RenderToStringResult> RenderToString(string applicationBasePath, Microsoft.AspNetCore.NodeServices.INodeServices nodeServices, System.Threading.CancellationToken applicationStoppingToken, Microsoft.AspNetCore.SpaServices.Prerendering.JavaScriptModuleExport bootModule, string requestAbsoluteUrl, string requestPathAndQuery, object customDataParameter, int timeoutMilliseconds, string requestPathBase) { throw null; }
}
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute(Attributes="asp-prerender-module")]
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class PrerenderTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
{
public PrerenderTagHelper(System.IServiceProvider serviceProvider) { }
@ -47,6 +55,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
[System.Diagnostics.DebuggerStepThroughAttribute]
public override System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { throw null; }
}
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class RenderToStringResult
{
public RenderToStringResult() { }
@ -59,6 +68,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
}
namespace Microsoft.AspNetCore.SpaServices.Webpack
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public partial class WebpackDevMiddlewareOptions
{
public WebpackDevMiddlewareOptions() { }
@ -75,8 +85,10 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack
}
namespace Microsoft.Extensions.DependencyInjection
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static partial class PrerenderingServiceCollectionExtensions
{
[System.ObsoleteAttribute("Use Microsoft.AspNetCore.SpaServices.Extensions")]
public static void AddSpaPrerenderer(this Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection) { }
}
}

View File

@ -1,233 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
yarn.lock
wwwroot/dist
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/

View File

@ -1,52 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.SpaServices.Prerendering;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Webpack.ActionResults
{
// This is an example of how you could invoke the prerendering API from an ActionResult, so as to
// prerender a SPA component as the entire response page (instead of injecting the SPA component
// into a Razor view's output)
public class PrerenderResult : ActionResult
{
private JavaScriptModuleExport _moduleExport;
private object _dataToSupply;
public PrerenderResult(JavaScriptModuleExport moduleExport, object dataToSupply = null)
{
_moduleExport = moduleExport;
_dataToSupply = dataToSupply;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
var nodeServices = context.HttpContext.RequestServices.GetRequiredService<INodeServices>();
var hostEnv = context.HttpContext.RequestServices.GetRequiredService<IWebHostEnvironment>();
var applicationLifetime = context.HttpContext.RequestServices.GetRequiredService<IHostApplicationLifetime>();
var applicationBasePath = hostEnv.ContentRootPath;
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;
var prerenderedHtml = await Prerenderer.RenderToString(
applicationBasePath,
nodeServices,
applicationLifetime.ApplicationStopping,
_moduleExport,
request.GetEncodedUrl(),
request.Path + request.QueryString.Value,
_dataToSupply,
/* timeoutMilliseconds */ 30000,
/* requestPathBase */ "/"
);
response.ContentType = "text/html";
await response.WriteAsync(prerenderedHtml.Html);
}
}
}

View File

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.Prerendering;
namespace Webpack.ActionResults
{
public static class PrerenderResultExtensions
{
public static PrerenderResult Prerender(this ControllerBase controller, JavaScriptModuleExport exportToPrerender, object dataToSupply = null)
{
return new PrerenderResult(exportToPrerender, dataToSupply);
}
}
}

View File

@ -1,4 +0,0 @@
import { HelloWorld } from './HelloWorld';
import './styles/main.less';
new HelloWorld().doIt();

View File

@ -1,5 +0,0 @@
export class HelloWorld {
public doIt() {
console.log('Hello from MyApp');
}
}

View File

@ -1,5 +0,0 @@
@headerColor: red;
h1 {
color: @headerColor;
}

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Webpack.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
}

View File

@ -1,59 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
using Microsoft.AspNetCore.NodeServices;
namespace Webpack
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddNodeServices();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
// For real apps, you should only use Webpack Dev Middleware at development time. For production,
// you'll get better performance and reliability if you precompile the webpack output and simply
// serve the resulting static files. For examples of setting up this automatic switch between
// development-style and production-style webpack usage, see the 'templates' dir in this repo.
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true
});
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.ConfigureLogging(factory =>
{
factory.AddConsole();
factory.AddDebug();
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@ -1,10 +0,0 @@
@{
ViewData["Title"] = "Home Page";
}
<h1>Hello</h1>
Hi there. Enter some text: <input />
@section scripts {
<script src="dist/main.js"></script>
}

View File

@ -1,6 +0,0 @@
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<environment names="Production">
<link rel="stylesheet" href="dist/my-styles.css" />
</environment>
</head>
<body>
@RenderBody()
@RenderSection("scripts", required: false)
</body>
</html>

View File

@ -1,2 +0,0 @@
@using Webpack
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"

View File

@ -1,3 +0,0 @@
@{
Layout = "_Layout";
}

View File

@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0</TargetFrameworks>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.SpaServices" />
<Reference Include="Microsoft.AspNetCore.Diagnostics" />
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.Extensions.Logging.Debug" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
<Exec Command="npm install" />
</Target>
</Project>

View File

@ -1,10 +0,0 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

@ -1,20 +0,0 @@
{
"name": "Webpack",
"version": "0.0.0",
"devDependencies": {
"css-loader": "^0.23.1",
"extendify": "^1.0.0",
"extract-text-webpack-plugin": "^1.0.1",
"less": "^2.6.0",
"less-loader": "^2.2.2",
"style-loader": "^0.13.0",
"webpack-hot-middleware": "^2.7.1"
},
"dependencies": {
"aspnet-prerendering": "^1.0.4",
"aspnet-webpack": "^1.0.3",
"ts-loader": "^0.8.1",
"typescript": "^2.0.0",
"webpack": "^1.13.3"
}
}

View File

@ -1,13 +0,0 @@
{
"compilerOptions": {
"moduleResolution": "node",
"module": "commonjs",
"target": "es5",
"jsx": "preserve",
"sourceMap": true,
"lib": ["es6", "dom"]
},
"exclude": [
"node_modules"
]
}

Some files were not shown because too many files have changed in this diff Show More