diff --git a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
index f701d0a217..3da4706236 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
@@ -94,6 +94,22 @@ namespace Microsoft.AspNet.Razor.Runtime
return string.Format(CultureInfo.CurrentCulture, GetString("TagNameAttribute_AdditionalTagsCannotContainNull"), p0);
}
+ ///
+ /// The value cannot be null or empty.
+ ///
+ internal static string ArgumentCannotBeNullOrEmpty
+ {
+ get { return GetString("ArgumentCannotBeNullOrEmpty"); }
+ }
+
+ ///
+ /// The value cannot be null or empty.
+ ///
+ internal static string FormatArgumentCannotBeNullOrEmpty()
+ {
+ return GetString("ArgumentCannotBeNullOrEmpty");
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
index 8e0b4485bb..563b72d5a7 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
+++ b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
@@ -134,4 +134,7 @@
Parameter {0} must not contain null tag names.
+
+ The value cannot be null or empty.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlAttributeNameAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlAttributeNameAttribute.cs
new file mode 100644
index 0000000000..ccd2948a61
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlAttributeNameAttribute.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Runtime.TagHelpers
+{
+ ///
+ /// Used to override an property's HTML attribute name.
+ ///
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
+ public sealed class HtmlAttributeNameAttribute : Attribute
+ {
+ ///
+ /// Instantiates a new instance of the class.
+ ///
+ /// HTML attribute name for the associated property.
+ public HtmlAttributeNameAttribute(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(name));
+ }
+
+ Name = name;
+ }
+
+ ///
+ /// HTML attribute name of the associated property.
+ ///
+ public string Name { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs
index 558a835211..b3a0482f87 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs
@@ -68,11 +68,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return attributeDescriptors;
}
- // TODO: Make the HTML attribute name support names from a AttributeNameAttribute:
- // https://github.com/aspnet/Razor/issues/121
private static TagHelperAttributeDescriptor ToAttributeDescriptor(PropertyInfo property)
{
- return new TagHelperAttributeDescriptor(property.Name, property);
+ var attributeNameAttribute = property.GetCustomAttribute(inherit: false);
+ var attributeName = attributeNameAttribute != null ?
+ attributeNameAttribute.Name :
+ property.Name;
+
+ return new TagHelperAttributeDescriptor(attributeName, property);
}
private static ContentBehavior GetContentBehavior(Type type)
diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs
index fbbc3cf4f6..cae296645c 100644
--- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs
+++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs
@@ -8,7 +8,7 @@ using Microsoft.Internal.Web.Utils;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
- public class CompleteTagHelperDescriptorComparer : TagHelperDescriptorComparer
+ public class CompleteTagHelperDescriptorComparer : TagHelperDescriptorComparer, IEqualityComparer
{
public new static readonly CompleteTagHelperDescriptorComparer Default =
new CompleteTagHelperDescriptorComparer();
@@ -17,14 +17,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
}
- public new bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
+ bool IEqualityComparer.Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
return base.Equals(descriptorX, descriptorY) &&
descriptorX.Attributes.SequenceEqual(descriptorY.Attributes,
CompleteTagHelperAttributeDescriptorComparer.Default);
}
- public new int GetHashCode(TagHelperDescriptor descriptor)
+ int IEqualityComparer.GetHashCode(TagHelperDescriptor descriptor)
{
return HashCodeCombiner.Start()
.Add(base.GetHashCode())
diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs
index 0338609ece..eee3922e88 100644
--- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs
+++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs
@@ -8,6 +8,85 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class TagHelperDescriptorFactoryTest
{
+ [Fact]
+ public void CreateDescriptor_OverridesAttributeNameFromAttribute()
+ {
+ // Arrange
+ var validProperty1 = typeof(OverriddenAttributeTagHelper).GetProperty(
+ nameof(OverriddenAttributeTagHelper.ValidAttribute1));
+ var validProperty2 = typeof(OverriddenAttributeTagHelper).GetProperty(
+ nameof(OverriddenAttributeTagHelper.ValidAttribute2));
+ var expectedDescriptors = new[] {
+ new TagHelperDescriptor(
+ "OverriddenAttribute",
+ typeof(OverriddenAttributeTagHelper).FullName,
+ ContentBehavior.None,
+ new[] {
+ new TagHelperAttributeDescriptor("SomethingElse", validProperty1),
+ new TagHelperAttributeDescriptor("Something-Else", validProperty2)
+ })
+ };
+
+ // Act
+ var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(OverriddenAttributeTagHelper));
+
+ // Assert
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
+ }
+
+ [Fact]
+ public void CreateDescriptor_DoesNotInheritOverridenAttributeName()
+ {
+ // Arrange
+ var validProperty1 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty(
+ nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute1));
+ var validProperty2 = typeof(InheritedOverriddenAttributeTagHelper).GetProperty(
+ nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute2));
+ var expectedDescriptors = new[] {
+ new TagHelperDescriptor(
+ "InheritedOverriddenAttribute",
+ typeof(InheritedOverriddenAttributeTagHelper).FullName,
+ ContentBehavior.None,
+ new[] {
+ new TagHelperAttributeDescriptor(nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute1),
+ validProperty1),
+ new TagHelperAttributeDescriptor("Something-Else", validProperty2)
+ })
+ };
+
+ // Act
+ var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(InheritedOverriddenAttributeTagHelper));
+
+ // Assert
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
+ }
+
+ [Fact]
+ public void CreateDescriptor_AllowsOverridenAttributeNameOnUnimplementedVirtual()
+ {
+ // Arrange
+ var validProperty1 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty(
+ nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute1));
+ var validProperty2 = typeof(InheritedNotOverriddenAttributeTagHelper).GetProperty(
+ nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute2));
+ var expectedDescriptors = new[] {
+ new TagHelperDescriptor(
+ "InheritedNotOverriddenAttribute",
+ typeof(InheritedNotOverriddenAttributeTagHelper).FullName,
+ ContentBehavior.None,
+ new[] {
+ new TagHelperAttributeDescriptor("SomethingElse", validProperty1),
+ new TagHelperAttributeDescriptor("Something-Else", validProperty2)
+ })
+ };
+
+ // Act
+ var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(InheritedNotOverriddenAttributeTagHelper));
+
+ // Assert
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
+ }
+
[Fact]
public void CreateDescriptor_BuildsDescriptorsFromSimpleTypes()
{
@@ -19,7 +98,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -41,7 +120,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -62,7 +141,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -84,7 +163,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -107,7 +186,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -124,7 +203,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -142,7 +221,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -171,7 +250,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(MultiTagTagHelper));
// Assert
- Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -192,7 +271,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Assert
var descriptor = Assert.Single(descriptors);
- Assert.Equal(descriptor, expectedDescriptor, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -208,7 +287,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(DuplicateTagNameTagHelper));
// Assert
- Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -225,7 +304,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(OverrideNameTagHelper));
// Assert
- Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
}
[Fact]
@@ -242,7 +321,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(MultipleAttributeTagHelper));
// Assert
- Assert.Equal(descriptors, expectedDescriptors, CompleteTagHelperDescriptorComparer.Default);
+ Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default);
}
[ContentBehavior(ContentBehavior.Append)]
@@ -283,5 +362,23 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper
{
}
+
+ private class OverriddenAttributeTagHelper
+ {
+ [HtmlAttributeName("SomethingElse")]
+ public virtual string ValidAttribute1 { get; set; }
+
+ [HtmlAttributeName("Something-Else")]
+ public string ValidAttribute2 { get; set; }
+ }
+
+ private class InheritedOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
+ {
+ public override string ValidAttribute1 { get; set; }
+ }
+
+ private class InheritedNotOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
+ {
+ }
}
}
\ No newline at end of file