Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Url.Action overloads (and similar) ignore inherited properties.

This fix affects both WebAPI HttpRouteValueDictionary,
Overloads that take anonymous objects to construct a routevaluedictionary
and overload that takes anonymous objects to create HtmlAttributes.

The fix reverts the behavior to MVC5/WebAPI2/WebPages3 behavior where it will allow for derived properties to show up.
But it maintains the performance characteristics of MVC5.1/WevAPI2.1/WebPages3.1.
(cherry picked from commit 1e6b50c)
  • Loading branch information
Yishai Galatzer committed Jan 30, 2014
1 parent 1440514 commit 54866f0
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/Common/PropertyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ protected static PropertyHelper[] GetProperties(object instance,
{
// We avoid loading indexed properties using the where statement.
// Indexed properties are not useful (or valid) for grabbing properties off an anonymous object.
IEnumerable<PropertyInfo> properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
IEnumerable<PropertyInfo> properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.GetIndexParameters().Length == 0 &&
prop.GetMethod != null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static TheoryDataSet<object, Dictionary<string, object>> ObjectConstructo

[Theory]
[PropertyData("DictionaryConstructorData")]
public void Constructor_AcceptsDIctionaryValues(Dictionary<string, object> input, Dictionary<string, object> expectedOutput)
public void Constructor_AcceptsDictionaryValues(Dictionary<string, object> input, Dictionary<string, object> expectedOutput)
{
HttpRouteValueDictionary routeValues = new HttpRouteValueDictionary(input);
Assert.True(expectedOutput.SequenceEqual(routeValues));
Expand Down
136 changes: 98 additions & 38 deletions test/System.Web.WebPages.Test/Utils/PropertyHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,8 @@ public void PropertyHelperDoesNotChangeUnderscores()
// Arrange
var anonymous = new { bar_baz2 = "foo" };

// Act
PropertyHelper helper = PropertyHelper.GetProperties(anonymous).Single();

// Assert
// Act + Assert
PropertyHelper helper = Assert.Single(PropertyHelper.GetProperties(anonymous));
Assert.Equal("bar_baz2", helper.Name);
}

Expand All @@ -95,31 +93,11 @@ public void PropertyHelperDoesNotFindPrivateProperties()
// Arrange
var anonymous = new PrivateProperties();

// Act
PropertyHelper helper = PropertyHelper.GetProperties(anonymous).Single();

// Assert
// Act + Assert
PropertyHelper helper = Assert.Single(PropertyHelper.GetProperties(anonymous));
Assert.Equal("Prop1", helper.Name);
}

private class Derived : PrivateProperties
{
public int Prop4 { get; set; }
}

[Fact]
public void PropertyHelperDoesNotFindBaseClassProperties()
{
// Arrange
var anonymous = new Derived();

// Act
PropertyHelper helper = PropertyHelper.GetProperties(anonymous).Single();

// Assert
Assert.Equal("Prop4", helper.Name);
}

private class Static
{
public static int Prop2 { get; set; }
Expand All @@ -132,10 +110,8 @@ public void PropertyHelperDoesNotFindStaticProperties()
// Arrange
var anonymous = new Static();

// Act
PropertyHelper helper = PropertyHelper.GetProperties(anonymous).Single();

// Assert
// Act + Assert
PropertyHelper helper = Assert.Single(PropertyHelper.GetProperties(anonymous));
Assert.Equal("Prop5", helper.Name);
}

Expand All @@ -151,10 +127,8 @@ public void PropertyHelperDoesNotFindSetOnlyProperties()
// Arrange
var anonymous = new SetOnly();

// Act
PropertyHelper helper = PropertyHelper.GetProperties(anonymous).Single();

// Assert
// Act + Assert
PropertyHelper helper = Assert.Single(PropertyHelper.GetProperties(anonymous));
Assert.Equal("Prop6", helper.Name);
}

Expand All @@ -173,13 +147,99 @@ public void PropertyHelperWorksForStruct()
anonymous.IntProp = 3;
anonymous.StringProp = "Five";

// Act + Assert
PropertyHelper helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous).Where(prop => prop.Name == "IntProp"));
PropertyHelper helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous).Where(prop => prop.Name == "StringProp"));
Assert.Equal(3, helper1.GetValue(anonymous));
Assert.Equal("Five", helper2.GetValue(anonymous));
}

public class BaseClass
{
public string PropA { get; set; }

protected string PropProtected { get; set; }
}

public class DerivedClass : BaseClass
{
public string PropB { get; set; }
}

public class BaseClassWithVirtual
{
public virtual string PropA { get; set; }
public string PropB { get; set; }
}

public class DerivedClassWithNew : BaseClassWithVirtual
{
public new string PropB { get { return "Newed"; } }
}

public class DerivedClassWithOverride : BaseClassWithVirtual
{
public override string PropA { get { return "Overriden"; } }
}

[Fact]
public void PropertyHelperForDerivedClass()
{
// Arrange
object derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" };

// Act
PropertyHelper helper1 = PropertyHelper.GetProperties(anonymous).Where(prop => prop.Name == "IntProp").Single();
PropertyHelper helper2 = PropertyHelper.GetProperties(anonymous).Where(prop => prop.Name == "StringProp").Single();
PropertyHelper[] helpers = PropertyHelper.GetProperties(derived).ToArray();

// Assert
Assert.Equal(3, helper1.GetValue(anonymous));
Assert.Equal("Five", helper2.GetValue(anonymous));
Assert.NotNull(helpers);
Assert.Equal(2, helpers.Length);

PropertyHelper propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
PropertyHelper propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));

Assert.Equal("propAValue", propAHelper.GetValue(derived));
Assert.Equal("propBValue", propBHelper.GetValue(derived));
}

[Fact]
public void PropertyHelperForDerivedClassWithNew()
{
// Arrange
object derived = new DerivedClassWithNew { PropA = "propAValue" };

// Act
PropertyHelper[] helpers = PropertyHelper.GetProperties(derived).ToArray();

// Assert
Assert.NotNull(helpers);
Assert.Equal(2, helpers.Length);

PropertyHelper propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
PropertyHelper propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));

Assert.Equal("propAValue", propAHelper.GetValue(derived));
Assert.Equal("Newed", propBHelper.GetValue(derived));
}

[Fact]
public void PropertyHelperForDerivedWithVirtual()
{
// Arrange
object derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" };

// Act
PropertyHelper[] helpers = PropertyHelper.GetProperties(derived).ToArray();

// Assert
Assert.NotNull(helpers);
Assert.Equal(2, helpers.Length);

PropertyHelper propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA"));
PropertyHelper propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB"));

Assert.Equal("Overriden", propAHelper.GetValue(derived));
Assert.Equal("propBValue", propBHelper.GetValue(derived));
}
}
}

0 comments on commit 54866f0

Please sign in to comment.