diff --git a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs index 6876c97f0d..f8a28273e4 100644 --- a/src/Components/Components/src/Rendering/RenderTreeBuilder.cs +++ b/src/Components/Components/src/Rendering/RenderTreeBuilder.cs @@ -581,6 +581,14 @@ namespace Microsoft.AspNetCore.Components.Rendering /// An integer that represents the position of the instruction in the source code. public void OpenRegion(int sequence) { + // We are entering a new scope, since we track the "duplicate attributes" per + // element/component we might need to clean them up now. + if (_hasSeenAddMultipleAttributes) + { + var indexOfLastElementOrComponent = _openElementIndices.Peek(); + ProcessDuplicateAttributes(first: indexOfLastElementOrComponent + 1); + } + _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.Region(sequence)); } diff --git a/src/Components/Components/test/Rendering/RenderTreeBuilderTest.cs b/src/Components/Components/test/Rendering/RenderTreeBuilderTest.cs index 7dfce8a79c..31364dca00 100644 --- a/src/Components/Components/test/Rendering/RenderTreeBuilderTest.cs +++ b/src/Components/Components/test/Rendering/RenderTreeBuilderTest.cs @@ -298,6 +298,39 @@ namespace Microsoft.AspNetCore.Components.Rendering frame => AssertFrame.Attribute(frame, "attribute7", "the end")); } + [Fact] + public void CanAddMultipleAttributes_WithChildRegion() + { + // This represents bug https://github.com/aspnet/AspNetCore/issues/16570 + // If a sequence of attributes is terminated by a call to builder.OpenRegion, + // then the attribute deduplication logic wasn't working correctly + + // Arrange + var builder = new RenderTreeBuilder(); + + // Act + builder.OpenElement(0, "myelement"); + builder.AddAttribute(0, "attribute1", "value1"); + builder.AddMultipleAttributes(1, new Dictionary() + { + { "attribute1", "value2" }, + }); + builder.OpenRegion(2); + builder.OpenElement(3, "child"); + builder.CloseElement(); + builder.CloseRegion(); + builder.CloseElement(); + + // Assert + var frames = builder.GetFrames().AsEnumerable().ToArray(); + Assert.Collection( + frames, + frame => AssertFrame.Element(frame, "myelement", 4), + frame => AssertFrame.Attribute(frame, "attribute1", "value2"), + frame => AssertFrame.Region(frame, 2, 2), + frame => AssertFrame.Element(frame, "child", 1, 3)); + } + [Fact] public void CanAddMultipleAttributes_DictionaryObject() {