diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs index e4afcaed3a..f479c6ce6b 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs @@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp public void RenderTagHelper(TagHelperChunk chunk) { // Remove any duplicate TagHelperDescriptors that reference the same type name. Duplicates can occur when - // multiple TargetElement attributes are on a TagHelper type and matchs overlap for an HTML element. + // multiple TargetElement attributes are on a TagHelper type and matches overlap for an HTML element. // Having more than one descriptor with the same TagHelper type results in generated code that runs // the same TagHelper X many times (instead of once) over a single HTML element. var tagHelperDescriptors = chunk.Descriptors.Distinct(TypeNameTagHelperDescriptorComparer.Default); @@ -190,20 +190,21 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } } - private void RenderBoundHTMLAttributes(IDictionary chunkAttributes, + private void RenderBoundHTMLAttributes(IList> chunkAttributes, string tagHelperVariableName, IEnumerable attributeDescriptors, Dictionary htmlAttributeValues) { foreach (var attributeDescriptor in attributeDescriptors) { - Chunk attributeValueChunk; + var matchingAttributes = chunkAttributes.Where( + attr => string.Equals(attr.Key, attributeDescriptor.Name, StringComparison.OrdinalIgnoreCase)); - var providedAttribute = chunkAttributes.TryGetValue(attributeDescriptor.Name, - out attributeValueChunk); - - if (providedAttribute) + if (matchingAttributes.Any()) { + // First attribute wins, even if there's duplicates. + var attributeValueChunk = matchingAttributes.First().Value; + var attributeValueRecorded = htmlAttributeValues.ContainsKey(attributeDescriptor.Name); // Bufferable attributes are attributes that can have Razor code inside of them. diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperChunk.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperChunk.cs index 2db0761d18..5c2ec507f2 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperChunk.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperChunk.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler public TagHelperChunk( string tagName, bool selfClosing, - IDictionary attributes, + IList> attributes, IEnumerable descriptors) { TagName = tagName; @@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler /// These attributes are => so attribute values can consist /// of all sorts of Razor specific pieces. /// - public IDictionary Attributes { get; set; } + public IList> Attributes { get; set; } /// /// The s that are associated with the tag helpers HTML element. diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs index 8867ff21a3..ba7a8af60a 100644 --- a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs @@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Generator RazorResources.TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock); } - var attributes = new Dictionary(StringComparer.OrdinalIgnoreCase); + var attributes = new List>(); // We need to create a code generator to create chunks for each of the attributes. var codeGenerator = context.Host.CreateCodeGenerator( @@ -63,12 +63,13 @@ namespace Microsoft.AspNet.Razor.Generator var chunks = codeGenerator.Context.CodeTreeBuilder.CodeTree.Chunks; var first = chunks.FirstOrDefault(); - attributes[attribute.Key] = new ChunkBlock - { - Association = first?.Association, - Children = chunks, - Start = first == null ? SourceLocation.Zero : first.Start - }; + attributes.Add(new KeyValuePair(attribute.Key, + new ChunkBlock + { + Association = first?.Association, + Children = chunks, + Start = first == null ? SourceLocation.Zero : first.Start + })); // Reset the code tree builder so we can build a new one for the next attribute codeGenerator.Context.CodeTreeBuilder = new CodeTreeBuilder(); diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs index d294601b85..e6ec0eee81 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs @@ -133,7 +133,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator return new TagHelperChunk( tagName, selfClosing: false, - attributes: new Dictionary(), + attributes: new List>(), descriptors: tagHelperDescriptors) { Children = new List(),