Have ParameterView track its owning RenderBatchBuilder

This commit is contained in:
Steve Sanderson 2019-09-27 23:43:19 +01:00 committed by Artak
parent 2e7c8649b6
commit 612a6677b1
5 changed files with 34 additions and 28 deletions

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Components.Reflection; using Microsoft.AspNetCore.Components.Reflection;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components namespace Microsoft.AspNetCore.Components
@ -20,19 +21,24 @@ namespace Microsoft.AspNetCore.Components
RenderTreeFrame.Element(0, string.Empty).WithComponentSubtreeLength(1) RenderTreeFrame.Element(0, string.Empty).WithComponentSubtreeLength(1)
}; };
private static readonly ParameterView _empty = new ParameterView(_emptyFrames, 0, null); private static readonly ParameterView _empty = new ParameterView(null, _emptyFrames, 0, null);
// If a value is provided for this field, then the ParameterView instance is only
// valid for as long as the lifetime owner says it is
private readonly RenderBatchBuilder _lifetimeOwner;
private readonly RenderTreeFrame[] _frames; private readonly RenderTreeFrame[] _frames;
private readonly int _ownerIndex; private readonly int _ownerIndex;
private readonly IReadOnlyList<CascadingParameterState> _cascadingParametersOrNull; private readonly IReadOnlyList<CascadingParameterState> _cascadingParametersOrNull;
internal ParameterView(RenderTreeFrame[] frames, int ownerIndex) internal ParameterView(RenderBatchBuilder lifetimeOwnerOrNull, RenderTreeFrame[] frames, int ownerIndex)
: this(frames, ownerIndex, null) : this(lifetimeOwnerOrNull, frames, ownerIndex, null)
{ {
} }
private ParameterView(RenderTreeFrame[] frames, int ownerIndex, IReadOnlyList<CascadingParameterState> cascadingParametersOrNull) private ParameterView(RenderBatchBuilder lifetimeOwnerOrNull, RenderTreeFrame[] frames, int ownerIndex, IReadOnlyList<CascadingParameterState> cascadingParametersOrNull)
{ {
_lifetimeOwner = lifetimeOwnerOrNull;
_frames = frames; _frames = frames;
_ownerIndex = ownerIndex; _ownerIndex = ownerIndex;
_cascadingParametersOrNull = cascadingParametersOrNull; _cascadingParametersOrNull = cascadingParametersOrNull;
@ -108,7 +114,7 @@ namespace Microsoft.AspNetCore.Components
} }
internal ParameterView WithCascadingParameters(IReadOnlyList<CascadingParameterState> cascadingParameters) internal ParameterView WithCascadingParameters(IReadOnlyList<CascadingParameterState> cascadingParameters)
=> new ParameterView(_frames, _ownerIndex, cascadingParameters); => new ParameterView(_lifetimeOwner, _frames, _ownerIndex, cascadingParameters);
// It's internal because there isn't a known use case for user code comparing // It's internal because there isn't a known use case for user code comparing
// ParameterView instances, and even if there was, it's unlikely it should // ParameterView instances, and even if there was, it's unlikely it should
@ -215,7 +221,7 @@ namespace Microsoft.AspNetCore.Components
frames[++i] = RenderTreeFrame.Attribute(i, kvp.Key, kvp.Value); frames[++i] = RenderTreeFrame.Attribute(i, kvp.Key, kvp.Value);
} }
return new ParameterView(frames, 0); return new ParameterView(null, frames, 0);
} }
/// <summary> /// <summary>

View File

@ -364,8 +364,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// Handles the diff for attribute nodes only - this invariant is enforced by the caller. // Handles the diff for attribute nodes only - this invariant is enforced by the caller.
// //
// The diff for attributes is different because we allow attributes to appear in any order. // The diff for attributes is different because we allow attributes to appear in any order.
// Put another way, the attributes list of an element or component is *conceptually* // Put another way, the attributes list of an element or component is *conceptually*
// unordered. This is a case where we can produce a more minimal diff by avoiding // unordered. This is a case where we can produce a more minimal diff by avoiding
// non-meaningful reorderings of attributes. // non-meaningful reorderings of attributes.
private static void AppendAttributeDiffEntriesForRange( private static void AppendAttributeDiffEntriesForRange(
ref DiffContext diffContext, ref DiffContext diffContext,
@ -519,8 +519,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree
// comparisons it wants with the old values. Later we could choose to pass the // comparisons it wants with the old values. Later we could choose to pass the
// old parameter values if we wanted. By default, components always rerender // old parameter values if we wanted. By default, components always rerender
// after any SetParameters call, which is safe but now always optimal for perf. // after any SetParameters call, which is safe but now always optimal for perf.
var oldParameters = new ParameterView(oldTree, oldComponentIndex); var oldParameters = new ParameterView(null, oldTree, oldComponentIndex);
var newParameters = new ParameterView(newTree, newComponentIndex); var newParameters = new ParameterView(diffContext.BatchBuilder, newTree, newComponentIndex);
if (!newParameters.DefinitelyEquals(oldParameters)) if (!newParameters.DefinitelyEquals(oldParameters))
{ {
componentState.SetDirectParameters(newParameters); componentState.SetDirectParameters(newParameters);
@ -893,7 +893,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
var childComponentState = frame.ComponentState; var childComponentState = frame.ComponentState;
// Set initial parameters // Set initial parameters
var initialParameters = new ParameterView(frames, frameIndex); var initialParameters = new ParameterView(diffContext.BatchBuilder, frames, frameIndex);
childComponentState.SetDirectParameters(initialParameters); childComponentState.SetDirectParameters(initialParameters);
} }
@ -957,7 +957,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
/// Exists only so that the various methods in this class can call each other without /// Exists only so that the various methods in this class can call each other without
/// constantly building up long lists of parameters. Is private to this class, so the /// constantly building up long lists of parameters. Is private to this class, so the
/// fact that it's a mutable struct is manageable. /// fact that it's a mutable struct is manageable.
/// ///
/// Always pass by ref to avoid copying, and because the 'SiblingIndex' is mutable. /// Always pass by ref to avoid copying, and because the 'SiblingIndex' is mutable.
/// </summary> /// </summary>
private struct DiffContext private struct DiffContext

View File

@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
public void NotifyCascadingValueChanged() public void NotifyCascadingValueChanged()
{ {
var directParams = _latestDirectParametersSnapshot != null var directParams = _latestDirectParametersSnapshot != null
? new ParameterView(_latestDirectParametersSnapshot.Buffer, 0) ? new ParameterView(null, _latestDirectParametersSnapshot.Buffer, 0)
: ParameterView.Empty; : ParameterView.Empty;
var allParams = directParams.WithCascadingParameters(_cascadingParameters); var allParams = directParams.WithCascadingParameters(_cascadingParameters);
var task = Component.SetParametersAsync(allParams); var task = Component.SetParametersAsync(allParams);

View File

@ -673,7 +673,7 @@ namespace Microsoft.AspNetCore.Components
} }
builder.CloseComponent(); builder.CloseComponent();
var view = new ParameterView(builder.GetFrames().Array, ownerIndex: 0); var view = new ParameterView(null, builder.GetFrames().Array, ownerIndex: 0);
var cascadingParameters = new List<CascadingParameterState>(); var cascadingParameters = new List<CascadingParameterState>();
foreach (var kvp in _keyValuePairs) foreach (var kvp in _keyValuePairs)

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Components
{ {
RenderTreeFrame.ChildComponent(0, typeof(FakeComponent)).WithComponentSubtreeLength(1) RenderTreeFrame.ChildComponent(0, typeof(FakeComponent)).WithComponentSubtreeLength(1)
}; };
var parameters = new ParameterView(frames, 0); var parameters = new ParameterView(null, frames, 0);
// Assert // Assert
Assert.Empty(ToEnumerable(parameters)); Assert.Empty(ToEnumerable(parameters));
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Components
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(1) RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(1)
}; };
var parameters = new ParameterView(frames, 0); var parameters = new ParameterView(null, frames, 0);
// Assert // Assert
Assert.Empty(ToEnumerable(parameters)); Assert.Empty(ToEnumerable(parameters));
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Components
// end of the owner's descendants // end of the owner's descendants
RenderTreeFrame.Attribute(3, "orphaned attribute", "value") RenderTreeFrame.Attribute(3, "orphaned attribute", "value")
}; };
var parameters = new ParameterView(frames, 0); var parameters = new ParameterView(null, frames, 0);
// Assert // Assert
Assert.Collection(ToEnumerable(parameters), Assert.Collection(ToEnumerable(parameters),
@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Components
RenderTreeFrame.Element(3, "child element").WithElementSubtreeLength(2), RenderTreeFrame.Element(3, "child element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(4, "child attribute", "some value") RenderTreeFrame.Attribute(4, "child attribute", "some value")
}; };
var parameters = new ParameterView(frames, 0); var parameters = new ParameterView(null, frames, 0);
// Assert // Assert
Assert.Collection(ToEnumerable(parameters), Assert.Collection(ToEnumerable(parameters),
@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Components
var attribute1Value = new object(); var attribute1Value = new object();
var attribute2Value = new object(); var attribute2Value = new object();
var attribute3Value = new object(); var attribute3Value = new object();
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "attribute 1", attribute1Value) RenderTreeFrame.Attribute(1, "attribute 1", attribute1Value)
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Components
public void CanTryGetNonExistingValue() public void CanTryGetNonExistingValue()
{ {
// Arrange // Arrange
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "some other entry", new object()) RenderTreeFrame.Attribute(1, "some other entry", new object())
@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Components
public void CanTryGetExistingValueWithCorrectType() public void CanTryGetExistingValueWithCorrectType()
{ {
// Arrange // Arrange
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "my entry", "hello") RenderTreeFrame.Attribute(1, "my entry", "hello")
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Components
{ {
// Arrange // Arrange
var myEntryValue = new object(); var myEntryValue = new object();
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "my entry", myEntryValue), RenderTreeFrame.Attribute(1, "my entry", myEntryValue),
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Components
{ {
// Arrange // Arrange
var myEntryValue = new object(); var myEntryValue = new object();
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3),
RenderTreeFrame.Attribute(1, "my entry", myEntryValue), RenderTreeFrame.Attribute(1, "my entry", myEntryValue),
@ -188,7 +188,7 @@ namespace Microsoft.AspNetCore.Components
public void CanGetValueOrDefault_WithNonExistingValue() public void CanGetValueOrDefault_WithNonExistingValue()
{ {
// Arrange // Arrange
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "some other entry", new object()) RenderTreeFrame.Attribute(1, "some other entry", new object())
@ -209,7 +209,7 @@ namespace Microsoft.AspNetCore.Components
{ {
// Arrange // Arrange
var explicitDefaultValue = new DateTime(2018, 3, 20); var explicitDefaultValue = new DateTime(2018, 3, 20);
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "some other entry", new object()) RenderTreeFrame.Attribute(1, "some other entry", new object())
@ -226,7 +226,7 @@ namespace Microsoft.AspNetCore.Components
public void ThrowsIfTryGetExistingValueWithIncorrectType() public void ThrowsIfTryGetExistingValueWithIncorrectType()
{ {
// Arrange // Arrange
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "my entry", "hello") RenderTreeFrame.Attribute(1, "my entry", "hello")
@ -275,7 +275,7 @@ namespace Microsoft.AspNetCore.Components
{ {
// Arrange // Arrange
var entry2Value = new object(); var entry2Value = new object();
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(3),
RenderTreeFrame.Attribute(0, "entry 1", "value 1"), RenderTreeFrame.Attribute(0, "entry 1", "value 1"),
@ -304,7 +304,7 @@ namespace Microsoft.AspNetCore.Components
{ {
// Arrange // Arrange
var myEntryValue = new object(); var myEntryValue = new object();
var parameters = new ParameterView(new[] var parameters = new ParameterView(null, new[]
{ {
RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2), RenderTreeFrame.Element(0, "some element").WithElementSubtreeLength(2),
RenderTreeFrame.Attribute(1, "unrelated value", new object()) RenderTreeFrame.Attribute(1, "unrelated value", new object())