diff --git a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs index 517b4b65ab..bc6f82df29 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs @@ -99,6 +99,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree if (oldKey != null || newKey != null) { #region "Get diff action by matching on key" + // Regardless of whether these two keys match, since you are using keys, we want to validate at this point that there are no clashes + // so ensure we've built the dictionary that will be used for lookups if any don't match + keyedItemInfos ??= BuildKeyToInfoLookup(diffContext, origOldStartIndex, oldEndIndexExcl, origNewStartIndex, newEndIndexExcl); + if (Equals(oldKey, newKey)) { // Keys match @@ -108,11 +112,6 @@ namespace Microsoft.AspNetCore.Components.RenderTree else { // Keys don't match - if (keyedItemInfos == null) - { - keyedItemInfos = BuildKeyToInfoLookup(diffContext, origOldStartIndex, oldEndIndexExcl, origNewStartIndex, newEndIndexExcl); - } - var oldKeyItemInfo = oldKey != null ? keyedItemInfos[oldKey] : new KeyedItemInfo(-1, -1); var newKeyItemInfo = newKey != null ? keyedItemInfos[newKey] : new KeyedItemInfo(-1, -1); var oldKeyIsInNewTree = oldKeyItemInfo.NewIndex >= 0; diff --git a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs index 45e7fdcba1..503d45155f 100644 --- a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs +++ b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs @@ -360,6 +360,23 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message); } + [Fact] + public void RejectsClashingKeysEvenIfAllPairsMatch() + { + // This sort of scenario would happen if you accidentally used a constant value for @key + + // Arrange + AddWithKey(oldTree, "key1", "attrib1a"); + AddWithKey(oldTree, "key1", "attrib1b"); + + AddWithKey(newTree, "key1", "attrib1a"); + AddWithKey(newTree, "key1", "attrib1b"); + + // Act/Assert + var ex = Assert.Throws(() => GetSingleUpdatedComponent()); + Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message); + } + [Fact] public void HandlesInsertionOfUnkeyedItemsAroundKey() {