This repository has been archived by the owner on Jan 24, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated model binding to support fields as well as properties.
Added ModelBinding/BindingMemberInfo.cs to Nancy.csproj, migrating into it the logic DefaultBinder.cs uses to enumerate properties and expanding it to support fields as well. Added unit testing class Unit/ModelBinding/BindingMemberInfoFixture.cs to Nancy.Tests.csproj. Updated ModelBinding/BindingContext.cs to store an enumerable sequence of BindingMemberInfo objects instead of directly referencing System.Reflection.PropertyInfo. Updated all unit tests referencing this field accordingly. Updated ModelBinding/DefaultBinder.cs to work with BindingMemberInfo objects wherever it refers to model properties. The primary impact of this change is that .GetValue and .SetValue calls no longer need to supply a dummy 'null' parameter for the property's index arguments, as this detail (which does not apply, in any case, to fields) is now hidden within BindingMemberInfo. Updated the initialization of BindingContext objects in ModelBinding/DefaultBinder.cs to convert the enumerable list of properties to a list before stashing it in the ValidModelProperties member, because it does get enumerated more than once in some circumstances. Reworked the GetProperties helper method in ModelBinding/DefaultBinder.cs to leverage the new BindingMemberInfo.Collect method, leaving only the blacklist to be implemented within the method. Updated the Unit/ModelBinding/DefaultBinderFixture.cs and Unit/ModelBinding/DefaultBodyDeserializers/JsonBodyDeserializerFixture.cs unit test classes in Nancy.Tests to verify the functionality of the BindingMemberInfo objects on both properties and fields. Refactored the copy/paste overloads of BindProperty in ModelBinding/DefaultBinder.cs so that one overload calls the other. Fixed a bug in the BindProperty overload in ModelBinding/DefaultBinder.cs that takes a specified instance for the target of the binding that may have caused certain assignments to go to the wrong object. Renamed GetProperties to GetBindingMembers, BindProperty to BindValue and SetPropertyValue to SetBindingMemberValue in ModelBinding/DefaultBinder.cs.
- Loading branch information
Showing
9 changed files
with
602 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
235 changes: 235 additions & 0 deletions
235
src/Nancy.Tests/Unit/ModelBinding/BindingMemberInfoFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
namespace Nancy.Tests.Unit.ModelBinding | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Xml.Serialization; | ||
using Nancy.ModelBinding; | ||
using Xunit; | ||
using Xunit.Sdk; | ||
|
||
public class BindingMemberInfoFixture | ||
{ | ||
[Fact] | ||
public void Should_return_MemberInfo_for_properties_or_fields() | ||
{ | ||
// Given | ||
var type = typeof(TestModel); | ||
var underlyingFieldInfo = type.GetFields().First(); | ||
var underlyingPropertyInfo = type.GetProperties().First(); | ||
|
||
// When | ||
var fieldInfo = new BindingMemberInfo(underlyingFieldInfo); | ||
var propertyInfo = new BindingMemberInfo(underlyingPropertyInfo); | ||
|
||
// Then | ||
fieldInfo.MemberInfo.ShouldEqual(underlyingFieldInfo); | ||
propertyInfo.MemberInfo.ShouldEqual(underlyingPropertyInfo); | ||
} | ||
|
||
[Fact] | ||
public void Should_return_Name_for_properties_or_fields() | ||
{ | ||
// Given | ||
var type = typeof(TestModel); | ||
var underlyingFieldInfo = type.GetFields().First(); | ||
var underlyingPropertyInfo = type.GetProperties().First(); | ||
|
||
// When | ||
var fieldInfo = new BindingMemberInfo(underlyingFieldInfo); | ||
var propertyInfo = new BindingMemberInfo(underlyingPropertyInfo); | ||
|
||
// Then | ||
fieldInfo.Name.ShouldEqual(underlyingFieldInfo.Name); | ||
propertyInfo.Name.ShouldEqual(underlyingPropertyInfo.Name); | ||
} | ||
|
||
[Fact] | ||
public void Should_return_PropertyType_for_properties_or_fields() | ||
{ | ||
// Given | ||
var properties = BindingMemberInfo.Collect<TestModel>(); | ||
|
||
// When | ||
|
||
// Then | ||
properties.ShouldHaveCount(4); | ||
|
||
foreach (var propInfo in properties) | ||
{ | ||
if (propInfo.Name.StartsWith("Int")) | ||
{ | ||
propInfo.PropertyType.ShouldEqual(typeof(int)); | ||
} | ||
else if (propInfo.Name.StartsWith("String")) | ||
{ | ||
propInfo.PropertyType.ShouldEqual(typeof(string)); | ||
} | ||
else | ||
{ | ||
throw new AssertException("Internal error in unit test: Test model property/field name does not follow the expected convention: " + propInfo.Name); | ||
} | ||
} | ||
} | ||
|
||
[Fact] | ||
public void Should_get_fields() | ||
{ | ||
// Given | ||
var propInfo = BindingMemberInfo.Collect<TestModel>().Where(prop => prop.Name.EndsWith("Field")); | ||
var model = new TestModel(); | ||
|
||
// When | ||
model.IntField = 669; | ||
model.StringField = "testing"; | ||
|
||
// Then | ||
propInfo.Single(prop => prop.PropertyType == typeof(int)) | ||
.GetValue(model) | ||
.ShouldEqual(669); | ||
|
||
propInfo.Single(prop => prop.PropertyType == typeof(string)) | ||
.GetValue(model) | ||
.ShouldEqual("testing"); | ||
} | ||
|
||
[Fact] | ||
public void Should_set_fields() | ||
{ | ||
// Given | ||
var propInfo = BindingMemberInfo.Collect<TestModel>().Where(prop => prop.Name.EndsWith("Field")); | ||
var model = new TestModel(); | ||
|
||
// When | ||
propInfo.Single(prop => prop.PropertyType == typeof(int)) | ||
.SetValue(model, 42); | ||
|
||
propInfo.Single(prop => prop.PropertyType == typeof(string)) | ||
.SetValue(model, "nineteen"); | ||
|
||
// Then | ||
model.IntField.ShouldEqual(42); | ||
model.StringField.ShouldEqual("nineteen"); | ||
} | ||
|
||
[Fact] | ||
public void Should_get_properties() | ||
{ | ||
// Given | ||
var propInfo = BindingMemberInfo.Collect<TestModel>().Where(prop => prop.Name.EndsWith("Property")); | ||
var model = new TestModel(); | ||
|
||
// When | ||
model.IntProperty = 1701; | ||
model.StringProperty = "NancyFX Unit Testing"; | ||
|
||
// Then | ||
propInfo.Single(prop => prop.PropertyType == typeof(int)) | ||
.GetValue(model) | ||
.ShouldEqual(1701); | ||
|
||
propInfo.Single(prop => prop.PropertyType == typeof(string)) | ||
.GetValue(model) | ||
.ShouldEqual("NancyFX Unit Testing"); | ||
} | ||
|
||
[Fact] | ||
public void Should_set_properties() | ||
{ | ||
// Given | ||
var propInfo = BindingMemberInfo.Collect<TestModel>().Where(prop => prop.Name.EndsWith("Property")); | ||
var model = new TestModel(); | ||
|
||
// When | ||
propInfo.Single(prop => prop.PropertyType == typeof(int)) | ||
.SetValue(model, 2600); | ||
|
||
propInfo.Single(prop => prop.PropertyType == typeof(string)) | ||
.SetValue(model, "R2D2"); | ||
|
||
// Then | ||
model.IntProperty.ShouldEqual(2600); | ||
model.StringProperty.ShouldEqual("R2D2"); | ||
} | ||
|
||
[Fact] | ||
public void Should_collect_all_bindable_members_and_skip_all_others() | ||
{ | ||
// Given | ||
|
||
// When | ||
var properties = BindingMemberInfo.Collect<BiggerTestModel>(); | ||
|
||
// Then | ||
properties.ShouldHaveCount(16); | ||
|
||
foreach (var property in properties) | ||
property.Name.ShouldStartWith("Bindable"); | ||
} | ||
|
||
public class TestModel | ||
{ | ||
public int IntField; | ||
public string StringField; | ||
|
||
public int IntProperty { get; set; } | ||
public string StringProperty { get; set; } | ||
} | ||
|
||
public class BiggerTestModel | ||
{ | ||
public int BindableIntField; | ||
public int BindableIntProperty { get; set; } | ||
public string BindableStringField; | ||
public string BindableStringProperty { get; set; } | ||
public TestModel BindableTestModelField; | ||
public TestModel BindableTestModelProperty { get; set; } | ||
public BiggerTestModel BindableBiggerTestModelField; | ||
public BiggerTestModel BindableBiggerTestModelProperty { get; set; } | ||
[XmlIgnore] | ||
public IEnumerable<int> BindableEnumerableField; | ||
[XmlIgnore] | ||
public IEnumerable<int> BindableEnumerableProperty { get; set; } | ||
public List<string> BindableListField; | ||
public List<string> BindableListProperty { get; set; } | ||
public double[] BindableArrayField; | ||
public double[] BindableArrayProperty { get; set; } | ||
public TestModel[] BindableArrayOfObjectsField; | ||
public TestModel[] BindableArrayOfObjectsProperty { get; set; } | ||
|
||
public int this[int index] | ||
{ | ||
get { return 0; } | ||
set { } | ||
} | ||
|
||
public int this[string index, int index2] | ||
{ | ||
get { return 0; } | ||
set { } | ||
} | ||
|
||
public readonly int UnbindableReadOnlyField = 74205; | ||
|
||
public string UnbindableReadOnlyProperty | ||
{ | ||
get { return "hi"; } | ||
} | ||
|
||
public string UnbindableWriteOnlyProperty | ||
{ | ||
set { } | ||
} | ||
|
||
private int UnbindablePrivateField; | ||
|
||
public static int UnbindableStaticField; | ||
|
||
public static int UnbindableStaticProperty | ||
{ | ||
get { return 0; } | ||
set { } | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.