You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
2907 lines
114 KiB
C#
2907 lines
114 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.Web.UnitTestUtil;
|
|
using Moq;
|
|
using Moq.Protected;
|
|
using Xunit;
|
|
using Assert = Microsoft.TestCommon.AssertEx;
|
|
|
|
namespace System.Web.Mvc.Test
|
|
{
|
|
[CLSCompliant(false)]
|
|
public class DefaultModelBinderTest
|
|
{
|
|
[Fact]
|
|
public void BindComplexElementalModelReturnsIfOnModelUpdatingReturnsFalse()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
MyModel model = new MyModel() { ReadWriteProperty = 3 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
};
|
|
|
|
Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
|
|
mockHelper.Setup(b => b.PublicOnModelUpdating(controllerContext, It.IsAny<ModelBindingContext>())).Returns(false);
|
|
DefaultModelBinderHelper helper = mockHelper.Object;
|
|
|
|
// Act
|
|
helper.BindComplexElementalModel(controllerContext, bindingContext, model);
|
|
|
|
// Assert
|
|
Assert.Equal(3, model.ReadWriteProperty);
|
|
mockHelper.Verify();
|
|
mockHelper.Verify(b => b.PublicGetModelProperties(controllerContext, It.IsAny<ModelBindingContext>()), Times.Never());
|
|
mockHelper.Verify(b => b.PublicBindProperty(controllerContext, It.IsAny<ModelBindingContext>(), It.IsAny<PropertyDescriptor>()), Times.Never());
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindArrays()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0]", null },
|
|
{ "foo[1]", null },
|
|
{ "foo[2]", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var newIntArray = Assert.IsType<int[]>(newModel);
|
|
Assert.Equal(new[] { 0, 1, 2 }, newIntArray);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindCollections()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IList<int>)),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0]", null },
|
|
{ "foo[1]", null },
|
|
{ "foo[2]", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var modelAsList = Assert.IsAssignableFrom<IList<int>>(newModel);
|
|
Assert.Equal(new[] { 0, 1, 2 }, modelAsList.ToArray());
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindDictionariesWithDotsNotation()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
|
|
ModelName = "countries",
|
|
PropertyFilter = _ => true,
|
|
ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
|
|
{
|
|
{ "countries.CA.Name", "Canada" },
|
|
{ "countries.CA.States[0]", "Québec" },
|
|
{ "countries.CA.States[1]", "British Columbia" },
|
|
{ "countries.US.Name", "United States" },
|
|
{ "countries.US.States[0]", "Washington" },
|
|
{ "countries.US.States[1]", "Oregon" }
|
|
}, CultureInfo.CurrentCulture)
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
|
|
Assert.Equal(2, modelAsDictionary.Count);
|
|
Assert.Equal("Canada", modelAsDictionary["CA"].Name);
|
|
Assert.Equal("United States", modelAsDictionary["US"].Name);
|
|
Assert.Equal(2, modelAsDictionary["CA"].States.Count());
|
|
Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
|
|
Assert.True(modelAsDictionary["CA"].States.Contains("British Columbia"));
|
|
Assert.Equal(2, modelAsDictionary["US"].States.Count());
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindDictionariesWithBracketsNotation()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
|
|
ModelName = "countries",
|
|
PropertyFilter = _ => true,
|
|
ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
|
|
{
|
|
{ "countries[CA].Name", "Canada" },
|
|
{ "countries[CA].States[0]", "Québec" },
|
|
{ "countries[CA].States[1]", "British Columbia" },
|
|
{ "countries[US].Name", "United States" },
|
|
{ "countries[US].States[0]", "Washington" },
|
|
{ "countries[US].States[1]", "Oregon" }
|
|
}, CultureInfo.CurrentCulture)
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
|
|
Assert.Equal(2, modelAsDictionary.Count);
|
|
Assert.Equal("Canada", modelAsDictionary["CA"].Name);
|
|
Assert.Equal("United States", modelAsDictionary["US"].Name);
|
|
Assert.Equal(2, modelAsDictionary["CA"].States.Count());
|
|
Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
|
|
Assert.True(modelAsDictionary["CA"].States.Contains("British Columbia"));
|
|
Assert.Equal(2, modelAsDictionary["US"].States.Count());
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindDictionariesWithBracketsAndDotsNotation()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<string, CountryState>)),
|
|
ModelName = "countries",
|
|
PropertyFilter = _ => true,
|
|
ValueProvider = new DictionaryValueProvider<object>(new Dictionary<string, object>()
|
|
{
|
|
{ "countries[CA].Name", "Canada" },
|
|
{ "countries[CA].States[0]", "Québec" },
|
|
{ "countries.CA.States[1]", "British Columbia" },
|
|
{ "countries.US.Name", "United States" },
|
|
{ "countries.US.States[0]", "Washington" },
|
|
{ "countries.US.States[1]", "Oregon" }
|
|
}, CultureInfo.CurrentCulture)
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<string, CountryState>>(newModel);
|
|
Assert.Equal(2, modelAsDictionary.Count);
|
|
Assert.Equal("Canada", modelAsDictionary["CA"].Name);
|
|
Assert.Equal("United States", modelAsDictionary["US"].Name);
|
|
Assert.Equal(1, modelAsDictionary["CA"].States.Count());
|
|
Assert.True(modelAsDictionary["CA"].States.Contains("Québec"));
|
|
|
|
// We do not accept double notation for a same entry, so we can't find that state.
|
|
Assert.False(modelAsDictionary["CA"].States.Contains("British Columbia"));
|
|
Assert.Equal(2, modelAsDictionary["US"].States.Count());
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Washington"));
|
|
Assert.True(modelAsDictionary["US"].States.Contains("Oregon"));
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindDictionaries()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IDictionary<int, string>)),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0].key", null }, { "foo[0].value", null },
|
|
{ "foo[1].key", null }, { "foo[1].value", null },
|
|
{ "foo[2].key", null }, { "foo[2].value", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
|
mockIntBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(new ModelBindingContext().PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10;
|
|
});
|
|
|
|
Mock<IModelBinder> mockStringBinder = new Mock<IModelBinder>();
|
|
mockStringBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(string), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return (Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10) + "Value";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockIntBinder.Object },
|
|
{ typeof(string), mockStringBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
var modelAsDictionary = Assert.IsAssignableFrom<IDictionary<int, string>>(newModel);
|
|
Assert.Equal(3, modelAsDictionary.Count);
|
|
Assert.Equal("10Value", modelAsDictionary[10]);
|
|
Assert.Equal("11Value", modelAsDictionary[11]);
|
|
Assert.Equal("12Value", modelAsDictionary[12]);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelCanBindObjects()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return bc.ModelName + "PostValue";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(string), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal("FooPostValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelReturnsNullArrayIfNoValuesProvided()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider() { { "foo", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc) { return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture); });
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object newModel = binder.BindComplexModel(null, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Null(newModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelWhereModelTypeContainsBindAttribute()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelWithBindAttribute model = new ModelWithBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return bc.ModelName + "PostValue";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(string), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("FooPreValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindComplexModelWhereModelTypeDoesNotContainBindAttribute()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return bc.ModelName + "PostValue";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(string), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
binder.BindComplexModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("FooPostValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
// BindModel tests
|
|
|
|
[Fact]
|
|
public void BindModelCanBindObjects()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "Foo", null }, { "Bar", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return bc.ModelName + "PostValue";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(string), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal("FooPostValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelCanBindSimpleTypes()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo", "42" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal(42, updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModel_PerformsValidationByDefault()
|
|
{
|
|
// Arrange
|
|
ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
|
|
|
|
ControllerContext controllerContext = new ControllerContext();
|
|
controllerContext.Controller = new SimpleController();
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = metadata,
|
|
ModelName = "foo",
|
|
ValueProvider = new CustomUnvalidatedValueProvider()
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("fooValidated", updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModel_SkipsValidationIfControllerOptsOut()
|
|
{
|
|
// Arrange
|
|
ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
|
|
|
|
ControllerContext controllerContext = new ControllerContext();
|
|
controllerContext.Controller = new SimpleController();
|
|
controllerContext.Controller.ValidateRequest = false;
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = metadata,
|
|
ModelName = "foo",
|
|
ValueProvider = new CustomUnvalidatedValueProvider()
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("fooUnvalidated", updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModel_SkipsValidationIfModelOptsOut()
|
|
{
|
|
// Arrange
|
|
ModelMetadata metadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(string));
|
|
metadata.RequestValidationEnabled = false;
|
|
|
|
ControllerContext controllerContext = new ControllerContext();
|
|
controllerContext.Controller = new SimpleController();
|
|
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = metadata,
|
|
ModelName = "foo",
|
|
ValueProvider = new CustomUnvalidatedValueProvider()
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(controllerContext, bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("fooUnvalidated", updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelReturnsNullIfKeyNotFound()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Null(returnedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelThrowsIfBindingContextIsNull()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act & assert
|
|
Assert.ThrowsArgumentNull(
|
|
delegate { binder.BindModel(new ControllerContext(), null); }, "bindingContext");
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelValuesCanBeOverridden()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new ModelWithoutBindAttribute(), typeof(ModelWithoutBindAttribute)),
|
|
ModelName = "",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo", "FooPostValue" },
|
|
{ "bar", "BarPostValue" },
|
|
{ "baz", "BazPostValue" }
|
|
}
|
|
};
|
|
Mock<DefaultModelBinder> binder = new Mock<DefaultModelBinder> { CallBase = true };
|
|
binder.Protected().Setup<object>("GetPropertyValue",
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>(),
|
|
ItExpr.IsAny<PropertyDescriptor>(), ItExpr.IsAny<IModelBinder>())
|
|
.Returns("Hello, world!");
|
|
|
|
// Act
|
|
ModelWithoutBindAttribute model = (ModelWithoutBindAttribute)binder.Object.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Equal("Hello, world!", model.Bar);
|
|
Assert.Equal("Hello, world!", model.Baz);
|
|
Assert.Equal("Hello, world!", model.Foo);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelWithTypeConversionErrorUpdatesModelStateMessage()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new PropertyTestingModel(), typeof(PropertyTestingModel)),
|
|
ModelName = "",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "IntReadWrite", "foo" }
|
|
},
|
|
};
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
PropertyTestingModel model = (PropertyTestingModel)binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
ModelState modelState = bindingContext.ModelState["IntReadWrite"];
|
|
Assert.NotNull(modelState);
|
|
Assert.Single(modelState.Errors);
|
|
Assert.Equal("The value 'foo' is not valid for IntReadWrite.", modelState.Errors[0].ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelWithPrefix()
|
|
{
|
|
// Arrange
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "prefix",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "prefix.foo", "FooPostValue" },
|
|
{ "prefix.bar", "BarPostValue" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal("FooPostValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelWithPrefixAndFallback()
|
|
{
|
|
// Arrange
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
FallbackToEmptyPrefix = true,
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "prefix",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo", "FooPostValue" },
|
|
{ "bar", "BarPostValue" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal("FooPostValue", model.Foo);
|
|
Assert.Equal("BarPostValue", model.Bar);
|
|
Assert.Equal("BazPreValue", model.Baz);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelWithPrefixReturnsNullIfFallbackNotSpecifiedAndValueProviderContainsNoEntries()
|
|
{
|
|
// Arrange
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute()
|
|
{
|
|
Foo = "FooPreValue",
|
|
Bar = "BarPreValue",
|
|
Baz = "BazPreValue",
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "prefix",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo", "FooPostValue" },
|
|
{ "bar", "BarPostValue" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Null(updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindModelReturnsNullIfSimpleTypeNotFound()
|
|
{
|
|
// DevDiv 216165: ModelBinders should not try and instantiate simple types
|
|
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(string)),
|
|
ModelName = "prefix",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "prefix.foo", "foo" },
|
|
{ "prefix.bar", "bar" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
Assert.Null(updatedModel);
|
|
}
|
|
|
|
// BindProperty tests
|
|
|
|
[Fact]
|
|
public void BindPropertyCanUpdateComplexReadOnlyProperties()
|
|
{
|
|
// Arrange
|
|
// the Customer type contains a single read-only Address property
|
|
Customer model = new Customer();
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "Address", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Address address = (Address)bc.Model;
|
|
address.Street = "1 Microsoft Way";
|
|
address.Zip = "98052";
|
|
return address;
|
|
});
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["Address"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(Address), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Equal("1 Microsoft Way", model.Address.Street);
|
|
Assert.Equal("98052", model.Address.Zip);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindPropertyDoesNothingIfValueProviderContainsNoEntryForProperty()
|
|
{
|
|
// Arrange
|
|
MyModel2 model = new MyModel2() { IntReadWrite = 3 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Equal(3, model.IntReadWrite);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindProperty()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
MyModel2 model = new MyModel2() { IntReadWrite = 3 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "IntReadWrite", "42" }
|
|
}
|
|
};
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
|
|
|
|
Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
|
|
mockHelper.Setup(b => b.PublicOnPropertyValidating(controllerContext, bindingContext, pd, 42)).Returns(true).Verifiable();
|
|
mockHelper.Setup(b => b.PublicSetProperty(controllerContext, bindingContext, pd, 42)).Verifiable();
|
|
mockHelper.Setup(b => b.PublicOnPropertyValidated(controllerContext, bindingContext, pd, 42)).Verifiable();
|
|
DefaultModelBinderHelper helper = mockHelper.Object;
|
|
|
|
// Act
|
|
helper.PublicBindProperty(controllerContext, bindingContext, pd);
|
|
|
|
// Assert
|
|
mockHelper.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void BindPropertyReturnsIfOnPropertyValidatingReturnsFalse()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
MyModel2 model = new MyModel2() { IntReadWrite = 3 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "IntReadWrite", "42" }
|
|
}
|
|
};
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
|
|
|
|
Mock<DefaultModelBinderHelper> mockHelper = new Mock<DefaultModelBinderHelper>() { CallBase = true };
|
|
mockHelper.Setup(b => b.PublicOnPropertyValidating(controllerContext, bindingContext, pd, 42)).Returns(false);
|
|
DefaultModelBinderHelper helper = mockHelper.Object;
|
|
|
|
// Act
|
|
helper.PublicBindProperty(controllerContext, bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Equal(3, model.IntReadWrite);
|
|
mockHelper.Verify();
|
|
mockHelper.Verify(b => b.PublicSetProperty(controllerContext, bindingContext, pd, 42), Times.Never());
|
|
mockHelper.Verify(b => b.PublicOnPropertyValidated(controllerContext, bindingContext, pd, 42), Times.Never());
|
|
}
|
|
|
|
[Fact]
|
|
public void BindPropertySetsPropertyToNullIfUserLeftTextEntryFieldBlankForOptionalValue()
|
|
{
|
|
// Arrange
|
|
MyModel2 model = new MyModel2() { NullableIntReadWrite = 8 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "NullableIntReadWrite", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder.Setup(b => b.BindModel(new ControllerContext(), It.IsAny<ModelBindingContext>())).Returns((object)null);
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["NullableIntReadWrite"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int?), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Empty(bindingContext.ModelState);
|
|
Assert.Null(model.NullableIntReadWrite);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindPropertyUpdatesPropertyOnFailureIfInnerBinderReturnsNonNullObject()
|
|
{
|
|
// Arrange
|
|
MyModel2 model = new MyModel2() { IntReadWriteNonNegative = 8 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ValueProvider = new SimpleValueProvider() { { "IntReadWriteNonNegative", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
bc.ModelState.AddModelError("IntReadWriteNonNegative", "Some error text.");
|
|
return 4;
|
|
});
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWriteNonNegative"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
helper.PublicBindProperty(new ControllerContext(), bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Equal(false, bindingContext.ModelState.IsValidField("IntReadWriteNonNegative"));
|
|
var error = Assert.Single(bindingContext.ModelState["IntReadWriteNonNegative"].Errors);
|
|
Assert.Equal("Some error text.", error.ErrorMessage);
|
|
Assert.Equal(4, model.IntReadWriteNonNegative);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindPropertyUpdatesPropertyOnSuccess()
|
|
{
|
|
// Arrange
|
|
// Effectively, this is just testing updating a single property named "IntReadWrite"
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
MyModel2 model = new MyModel2() { IntReadWrite = 3 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
ModelState = new ModelStateDictionary() { { "blah", new ModelState() } },
|
|
ValueProvider = new SimpleValueProvider() { { "foo.IntReadWrite", null } }
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(3, bc.Model);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal("foo.IntReadWrite", bc.ModelName);
|
|
Assert.Equal(new ModelBindingContext().PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return 4;
|
|
});
|
|
|
|
PropertyDescriptor pd = TypeDescriptor.GetProperties(model)["IntReadWrite"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
helper.PublicBindProperty(controllerContext, bindingContext, pd);
|
|
|
|
// Assert
|
|
Assert.Equal(4, model.IntReadWrite);
|
|
}
|
|
|
|
// BindSimpleModel tests
|
|
|
|
[Fact]
|
|
public void BindSimpleModelCanReturnArrayTypes()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult(42, null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int[])),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
var returnedValueAsIntArray = Assert.IsType<int[]>(returnedValue);
|
|
Assert.Single(returnedValueAsIntArray);
|
|
Assert.Equal(42, returnedValueAsIntArray[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindSimpleModelCanReturnCollectionTypes()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult(new string[] { "42", "82" }, null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(IEnumerable<int>)),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
var returnedValueAsList = Assert.IsAssignableFrom<IEnumerable<int>>(returnedValue).ToList();
|
|
Assert.Equal(2, returnedValueAsList.Count);
|
|
Assert.Equal(42, returnedValueAsList[0]);
|
|
Assert.Equal(82, returnedValueAsList[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindSimpleModelCanReturnElementalTypes()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult("42", null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
Assert.Equal(42, returnedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindSimpleModelCanReturnStrings()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult(new object[] { "42" }, null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(string)),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
Assert.Equal("42", returnedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindSimpleModelChecksValueProviderResultRawValueType()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult(new MemoryStream(), null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Stream)),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
Assert.Equal(result, bindingContext.ModelState["foo"].Value);
|
|
Assert.Same(result.RawValue, returnedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void BindSimpleModelPropagatesErrorsOnFailure()
|
|
{
|
|
// Arrange
|
|
ValueProviderResult result = new ValueProviderResult("invalid", null, null);
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(int)),
|
|
ModelName = "foo",
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object returnedValue = binder.BindSimpleModel(null, bindingContext, result);
|
|
|
|
// Assert
|
|
Assert.False(bindingContext.ModelState.IsValidField("foo"));
|
|
Assert.IsType<InvalidOperationException>(bindingContext.ModelState["foo"].Errors[0].Exception);
|
|
Assert.Equal("The parameter conversion from type 'System.String' to type 'System.Int32' failed. See the inner exception for more information.", bindingContext.ModelState["foo"].Errors[0].Exception.Message);
|
|
Assert.Null(returnedValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateComplexElementalModelBindingContext_ReadsBindAttributeFromBuddyClass()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext originalBindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(CreateComplexElementalModelBindingContext_ReadsBindAttributeFromBuddyClass_Model)),
|
|
ModelName = "someName",
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
ModelBindingContext newBindingContext = binder.CreateComplexElementalModelBindingContext(new ControllerContext(), originalBindingContext, null);
|
|
|
|
// Assert
|
|
Assert.True(newBindingContext.PropertyFilter("foo"));
|
|
Assert.False(newBindingContext.PropertyFilter("bar"));
|
|
}
|
|
|
|
[MetadataType(typeof(CreateComplexElementalModelBindingContext_ReadsBindAttributeFromBuddyClass_Model_BuddyClass))]
|
|
private class CreateComplexElementalModelBindingContext_ReadsBindAttributeFromBuddyClass_Model
|
|
{
|
|
[Bind(Include = "foo")]
|
|
private class CreateComplexElementalModelBindingContext_ReadsBindAttributeFromBuddyClass_Model_BuddyClass
|
|
{
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateInstanceCreatesModelInstance()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
object modelObj = helper.PublicCreateModel(null, null, typeof(Guid));
|
|
|
|
// Assert
|
|
Assert.Equal(Guid.Empty, modelObj);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateInstanceCreatesModelInstanceForGenericICollection()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
object modelObj = helper.PublicCreateModel(null, null, typeof(ICollection<Guid>));
|
|
|
|
// Assert
|
|
Assert.IsAssignableFrom<ICollection<Guid>>(modelObj);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateInstanceCreatesModelInstanceForGenericIDictionary()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
object modelObj = helper.PublicCreateModel(null, null, typeof(IDictionary<string, Guid>));
|
|
|
|
// Assert
|
|
Assert.IsAssignableFrom<IDictionary<string, Guid>>(modelObj);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateInstanceCreatesModelInstanceForGenericIEnumerable()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
object modelObj = helper.PublicCreateModel(null, null, typeof(IEnumerable<Guid>));
|
|
|
|
// Assert
|
|
Assert.IsAssignableFrom<ICollection<Guid>>(modelObj);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateInstanceCreatesModelInstanceForGenericIList()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
object modelObj = helper.PublicCreateModel(null, null, typeof(IList<Guid>));
|
|
|
|
// Assert
|
|
Assert.IsAssignableFrom<IList<Guid>>(modelObj);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateSubIndexNameReturnsPrefixPlusIndex()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
string newName = helper.PublicCreateSubIndexName("somePrefix", 2);
|
|
|
|
// Assert
|
|
Assert.Equal("somePrefix[2]", newName);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateSubPropertyNameReturnsPrefixPlusPropertyName()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
string newName = helper.PublicCreateSubPropertyName("somePrefix", "someProperty");
|
|
|
|
// Assert
|
|
Assert.Equal("somePrefix.someProperty", newName);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateSubPropertyNameReturnsPropertyNameIfPrefixIsEmpty()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
string newName = helper.PublicCreateSubPropertyName(String.Empty, "someProperty");
|
|
|
|
// Assert
|
|
Assert.Equal("someProperty", newName);
|
|
}
|
|
|
|
[Fact]
|
|
public void CreateSubPropertyNameReturnsPropertyNameIfPrefixIsNull()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
string newName = helper.PublicCreateSubPropertyName(null, "someProperty");
|
|
|
|
// Assert
|
|
Assert.Equal("someProperty", newName);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetFilteredModelPropertiesFiltersNonUpdateableProperties()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(PropertyTestingModel)),
|
|
PropertyFilter = new BindAttribute() { Exclude = "Blacklisted" }.IsPropertyAllowed
|
|
};
|
|
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
PropertyDescriptorCollection properties = new PropertyDescriptorCollection(helper.PublicGetFilteredModelProperties(null, bindingContext).ToArray());
|
|
|
|
// Assert
|
|
Assert.NotNull(properties["StringReadWrite"]);
|
|
Assert.Null(properties["StringReadOnly"]);
|
|
Assert.NotNull(properties["IntReadWrite"]);
|
|
Assert.Null(properties["IntReadOnly"]);
|
|
Assert.NotNull(properties["ArrayReadWrite"]);
|
|
Assert.Null(properties["ArrayReadOnly"]);
|
|
Assert.NotNull(properties["AddressReadWrite"]);
|
|
Assert.NotNull(properties["AddressReadOnly"]);
|
|
Assert.NotNull(properties["Whitelisted"]);
|
|
Assert.Null(properties["Blacklisted"]);
|
|
Assert.Equal(6, properties.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelPropertiesReturnsUnfilteredPropertyList()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(PropertyTestingModel)),
|
|
PropertyFilter = new BindAttribute() { Exclude = "Blacklisted" }.IsPropertyAllowed
|
|
};
|
|
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
PropertyDescriptorCollection properties = helper.PublicGetModelProperties(null, bindingContext);
|
|
|
|
// Assert
|
|
Assert.NotNull(properties["StringReadWrite"]);
|
|
Assert.NotNull(properties["StringReadOnly"]);
|
|
Assert.NotNull(properties["IntReadWrite"]);
|
|
Assert.NotNull(properties["IntReadOnly"]);
|
|
Assert.NotNull(properties["ArrayReadWrite"]);
|
|
Assert.NotNull(properties["ArrayReadOnly"]);
|
|
Assert.NotNull(properties["AddressReadWrite"]);
|
|
Assert.NotNull(properties["AddressReadOnly"]);
|
|
Assert.NotNull(properties["Whitelisted"]);
|
|
Assert.NotNull(properties["Blacklisted"]);
|
|
Assert.Equal(10, properties.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsModelValidWithNullBindingContextThrows()
|
|
{
|
|
// Act & Assert
|
|
Assert.ThrowsArgumentNull(
|
|
() => DefaultModelBinderHelper.PublicIsModelValid(null),
|
|
"bindingContext");
|
|
}
|
|
|
|
[Fact]
|
|
public void IsModelValidReturnsModelStateIsValidWhenModelNameIsEmpty()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext contextWithNoErrors = new ModelBindingContext { ModelName = "" };
|
|
ModelBindingContext contextWithErrors = new ModelBindingContext { ModelName = "" };
|
|
contextWithErrors.ModelState.AddModelError("foo", "bar");
|
|
|
|
// Act & Assert
|
|
Assert.True(DefaultModelBinderHelper.PublicIsModelValid(contextWithNoErrors));
|
|
Assert.False(DefaultModelBinderHelper.PublicIsModelValid(contextWithErrors));
|
|
}
|
|
|
|
[Fact]
|
|
public void IsModelValidReturnsValidityOfSubModelStateWhenModelNameIsNotEmpty()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext contextWithNoErrors = new ModelBindingContext { ModelName = "foo" };
|
|
ModelBindingContext contextWithErrors = new ModelBindingContext { ModelName = "foo" };
|
|
contextWithErrors.ModelState.AddModelError("foo.bar", "baz");
|
|
ModelBindingContext contextWithUnrelatedErrors = new ModelBindingContext { ModelName = "foo" };
|
|
contextWithUnrelatedErrors.ModelState.AddModelError("biff", "baz");
|
|
|
|
// Act & Assert
|
|
Assert.True(DefaultModelBinderHelper.PublicIsModelValid(contextWithNoErrors));
|
|
Assert.False(DefaultModelBinderHelper.PublicIsModelValid(contextWithErrors));
|
|
Assert.True(DefaultModelBinderHelper.PublicIsModelValid(contextWithUnrelatedErrors));
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatingReturnsTrue()
|
|
{
|
|
// By default, this method does nothing, so we just want to make sure it returns true
|
|
|
|
// Arrange
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
bool returned = helper.PublicOnModelUpdating(null, null);
|
|
|
|
// Arrange
|
|
Assert.True(returned);
|
|
}
|
|
|
|
// OnModelUpdated tests
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedCalledWhenOnModelUpdatingReturnsTrue()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new ModelWithoutBindAttribute(), typeof(ModelWithoutBindAttribute)),
|
|
ModelName = "",
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
Mock<DefaultModelBinder> binder = new Mock<DefaultModelBinder> { CallBase = true };
|
|
binder.Protected().Setup<bool>("OnModelUpdating",
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>())
|
|
.Returns(true);
|
|
binder.Protected().Setup("OnModelUpdated",
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>())
|
|
.Verifiable();
|
|
|
|
// Act
|
|
binder.Object.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
binder.Verify();
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedNotCalledWhenOnModelUpdatingReturnsFalse()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new ModelWithoutBindAttribute(), typeof(ModelWithoutBindAttribute)),
|
|
ModelName = "",
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
Mock<DefaultModelBinder> binder = new Mock<DefaultModelBinder> { CallBase = true };
|
|
binder.Protected().Setup<bool>("OnModelUpdating",
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>())
|
|
.Returns(false);
|
|
|
|
// Act
|
|
binder.Object.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
binder.Verify();
|
|
binder.Protected().Verify("OnModelUpdated", Times.Never(),
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>());
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedDoesntAddNewMessagesWhenMessagesAlreadyExist()
|
|
{
|
|
// Arrange
|
|
var binder = new TestableDefaultModelBinder<SetPropertyModel>();
|
|
binder.Context.ModelState.AddModelError(BASE_MODEL_NAME + ".NonNullableStringWithAttribute", "Some pre-existing error");
|
|
|
|
// Act
|
|
binder.OnModelUpdated();
|
|
|
|
// Assert
|
|
var modelState = binder.Context.ModelState[BASE_MODEL_NAME + ".NonNullableStringWithAttribute"];
|
|
var error = Assert.Single(modelState.Errors);
|
|
Assert.Equal("Some pre-existing error", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnPropertyValidatingNotCalledOnPropertiesWithErrors()
|
|
{
|
|
// Arrange
|
|
ModelWithoutBindAttribute model = new ModelWithoutBindAttribute();
|
|
ModelBindingContext bindingContext = new ModelBindingContext
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo", "foo" }
|
|
},
|
|
};
|
|
bindingContext.ModelState.AddModelError("foo", "Pre-existing error");
|
|
Mock<DefaultModelBinder> binder = new Mock<DefaultModelBinder> { CallBase = true };
|
|
|
|
// Act
|
|
binder.Object.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
binder.Verify();
|
|
binder.Protected().Verify("OnPropertyValidating", Times.Never(),
|
|
ItExpr.IsAny<ControllerContext>(), ItExpr.IsAny<ModelBindingContext>(),
|
|
ItExpr.IsAny<PropertyDescriptor>(), ItExpr.IsAny<object>());
|
|
}
|
|
|
|
public class ExtraValueModel
|
|
{
|
|
public int RequiredValue { get; set; }
|
|
}
|
|
|
|
[Fact]
|
|
public void ExtraValueRequiredMessageNotAddedForAlreadyInvalidProperty()
|
|
{
|
|
// Arrange
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(MyModel)),
|
|
ModelName = "theModel",
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
bindingContext.ModelState.AddModelError("theModel.ReadWriteProperty", "Existing Error Message");
|
|
|
|
// Act
|
|
binder.BindModel(new ControllerContext(), bindingContext);
|
|
|
|
// Assert
|
|
ModelState modelState = bindingContext.ModelState["theModel.ReadWriteProperty"];
|
|
var error = Assert.Single(modelState.Errors);
|
|
Assert.Equal("Existing Error Message", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnPropertyValidatingReturnsTrueOnSuccess()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(MyModel)),
|
|
ModelName = "theModel"
|
|
};
|
|
|
|
PropertyDescriptor property = TypeDescriptor.GetProperties(typeof(MyModel))["ReadWriteProperty"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
bindingContext.PropertyMetadata["ReadWriteProperty"].Model = 42;
|
|
|
|
// Act
|
|
bool returned = helper.PublicOnPropertyValidating(new ControllerContext(), bindingContext, property, 42);
|
|
|
|
// Assert
|
|
Assert.True(returned);
|
|
Assert.Empty(bindingContext.ModelState);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateCollectionCreatesDefaultEntriesForInvalidElements()
|
|
{
|
|
// Arrange
|
|
List<int> model = new List<int>() { 4, 5, 6, 7, 8 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0]", null },
|
|
{ "foo[1]", null },
|
|
{ "foo[2]", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
int fooIdx = Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
return (fooIdx == 1) ? (object)null : fooIdx;
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateCollection(null, bindingContext, typeof(int));
|
|
|
|
// Assert
|
|
Assert.Equal(3, model.Count);
|
|
Assert.Equal(false, bindingContext.ModelState.IsValidField("foo[1]"));
|
|
Assert.Equal("A value is required.", bindingContext.ModelState["foo[1]"].Errors[0].ErrorMessage);
|
|
Assert.Equal(0, model[0]);
|
|
Assert.Equal(0, model[1]);
|
|
Assert.Equal(2, model[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateCollectionReturnsModifiedCollectionOnSuccess_ExplicitIndex()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
List<int> model = new List<int>() { 4, 5, 6, 7, 8 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo.index", new string[] { "alpha", "bravo", "charlie" } }, // 'bravo' will be skipped
|
|
{ "foo[alpha]", "10" },
|
|
{ "foo[charlie]", "30" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateCollection(controllerContext, bindingContext, typeof(int));
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal(2, model.Count);
|
|
Assert.Equal(10, model[0]);
|
|
Assert.Equal(30, model[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateCollectionReturnsModifiedCollectionOnSuccess_ZeroBased()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
List<int> model = new List<int>() { 4, 5, 6, 7, 8 };
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0]", null },
|
|
{ "foo[1]", null },
|
|
{ "foo[2]", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockInnerBinder = new Mock<IModelBinder>();
|
|
mockInnerBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockInnerBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateCollection(controllerContext, bindingContext, typeof(int));
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal(3, model.Count);
|
|
Assert.Equal(0, model[0]);
|
|
Assert.Equal(1, model[1]);
|
|
Assert.Equal(2, model[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateCollectionReturnsNullIfZeroIndexNotFound()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateCollection(null, bindingContext, typeof(object));
|
|
|
|
// Assert
|
|
Assert.Null(updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateDictionaryCreatesDefaultEntriesForInvalidValues()
|
|
{
|
|
// Arrange
|
|
Dictionary<string, int> model = new Dictionary<string, int>
|
|
{
|
|
{ "one", 1 },
|
|
{ "two", 2 }
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0].key", null }, { "foo[0].value", null },
|
|
{ "foo[1].key", null }, { "foo[1].value", null },
|
|
{ "foo[2].key", null }, { "foo[2].value", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockStringBinder = new Mock<IModelBinder>();
|
|
mockStringBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc) { return (Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10) + "Value"; });
|
|
|
|
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
|
mockIntBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
int fooIdx = Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
return (fooIdx == 1) ? (object)null : fooIdx;
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(string), mockStringBinder.Object },
|
|
{ typeof(int), mockIntBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateDictionary(null, bindingContext, typeof(string), typeof(int));
|
|
|
|
// Assert
|
|
Assert.Equal(3, model.Count);
|
|
Assert.False(bindingContext.ModelState.IsValidField("foo[1].value"));
|
|
Assert.Equal("A value is required.", bindingContext.ModelState["foo[1].value"].Errors[0].ErrorMessage);
|
|
Assert.Equal(0, model["10Value"]);
|
|
Assert.Equal(0, model["11Value"]);
|
|
Assert.Equal(2, model["12Value"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateDictionaryReturnsModifiedDictionaryOnSuccess_ExplicitIndex()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
Dictionary<int, string> model = new Dictionary<int, string>
|
|
{
|
|
{ 1, "one" },
|
|
{ 2, "two" }
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo.index", new string[] { "alpha", "bravo", "charlie" } }, // 'bravo' will be skipped
|
|
{ "foo[alpha].key", "10" }, { "foo[alpha].value", "ten" },
|
|
{ "foo[charlie].key", "30" }, { "foo[charlie].value", "thirty" }
|
|
}
|
|
};
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateDictionary(controllerContext, bindingContext, typeof(int), typeof(string));
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal(2, model.Count);
|
|
Assert.Equal("ten", model[10]);
|
|
Assert.Equal("thirty", model[30]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateDictionaryReturnsModifiedDictionaryOnSuccess_ZeroBased()
|
|
{
|
|
// Arrange
|
|
ControllerContext controllerContext = new Mock<ControllerContext>().Object;
|
|
|
|
Dictionary<int, string> model = new Dictionary<int, string>
|
|
{
|
|
{ 1, "one" },
|
|
{ 2, "two" }
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
PropertyFilter = _ => false,
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0].key", null }, { "foo[0].value", null },
|
|
{ "foo[1].key", null }, { "foo[1].value", null },
|
|
{ "foo[2].key", null }, { "foo[2].value", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
|
mockIntBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(int), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(new ModelBindingContext().PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10;
|
|
});
|
|
|
|
Mock<IModelBinder> mockStringBinder = new Mock<IModelBinder>();
|
|
mockStringBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
Assert.Equal(controllerContext, cc);
|
|
Assert.Equal(typeof(string), bc.ModelType);
|
|
Assert.Equal(bindingContext.ModelState, bc.ModelState);
|
|
Assert.Equal(bindingContext.PropertyFilter, bc.PropertyFilter);
|
|
Assert.Equal(bindingContext.ValueProvider, bc.ValueProvider);
|
|
return (Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10) + "Value";
|
|
});
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockIntBinder.Object },
|
|
{ typeof(string), mockStringBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateDictionary(controllerContext, bindingContext, typeof(int), typeof(string));
|
|
|
|
// Assert
|
|
Assert.Same(model, updatedModel);
|
|
Assert.Equal(3, model.Count);
|
|
Assert.Equal("10Value", model[10]);
|
|
Assert.Equal("11Value", model[11]);
|
|
Assert.Equal("12Value", model[12]);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateDictionaryReturnsNullIfNoValidElementsFound()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ValueProvider = new SimpleValueProvider()
|
|
};
|
|
DefaultModelBinder binder = new DefaultModelBinder();
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateDictionary(null, bindingContext, typeof(object), typeof(object));
|
|
|
|
// Assert
|
|
Assert.Null(updatedModel);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateDictionarySkipsInvalidKeys()
|
|
{
|
|
// Arrange
|
|
Dictionary<int, string> model = new Dictionary<int, string>
|
|
{
|
|
{ 1, "one" },
|
|
{ 2, "two" }
|
|
};
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "foo",
|
|
ValueProvider = new SimpleValueProvider()
|
|
{
|
|
{ "foo[0].key", null }, { "foo[0].value", null },
|
|
{ "foo[1].key", null }, { "foo[1].value", null },
|
|
{ "foo[2].key", null }, { "foo[2].value", null }
|
|
}
|
|
};
|
|
|
|
Mock<IModelBinder> mockIntBinder = new Mock<IModelBinder>();
|
|
mockIntBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc)
|
|
{
|
|
int fooIdx = Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture);
|
|
return (fooIdx == 1) ? (object)null : fooIdx;
|
|
});
|
|
|
|
Mock<IModelBinder> mockStringBinder = new Mock<IModelBinder>();
|
|
mockStringBinder
|
|
.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), It.IsAny<ModelBindingContext>()))
|
|
.Returns(
|
|
delegate(ControllerContext cc, ModelBindingContext bc) { return (Int32.Parse(bc.ModelName.Substring(4, 1), CultureInfo.InvariantCulture) + 10) + "Value"; });
|
|
|
|
DefaultModelBinder binder = new DefaultModelBinder()
|
|
{
|
|
Binders = new ModelBinderDictionary()
|
|
{
|
|
{ typeof(int), mockIntBinder.Object },
|
|
{ typeof(string), mockStringBinder.Object }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
object updatedModel = binder.UpdateDictionary(null, bindingContext, typeof(int), typeof(string));
|
|
|
|
// Assert
|
|
Assert.Equal(2, model.Count);
|
|
Assert.False(bindingContext.ModelState.IsValidField("foo[1].key"));
|
|
Assert.Equal("A value is required.", bindingContext.ModelState["foo[1].key"].Errors[0].ErrorMessage);
|
|
Assert.Equal("10Value", model[0]);
|
|
Assert.Equal("12Value", model[2]);
|
|
}
|
|
|
|
[ModelBinder(typeof(DefaultModelBinder))]
|
|
private class MyModel
|
|
{
|
|
public int ReadOnlyProperty
|
|
{
|
|
get { return 4; }
|
|
}
|
|
|
|
public int ReadWriteProperty { get; set; }
|
|
public int ReadWriteProperty2 { get; set; }
|
|
}
|
|
|
|
private class MyClassWithoutConverter
|
|
{
|
|
}
|
|
|
|
[Bind(Exclude = "Alpha,Echo")]
|
|
private class MyOtherModel
|
|
{
|
|
public string Alpha { get; set; }
|
|
public string Bravo { get; set; }
|
|
public string Charlie { get; set; }
|
|
public string Delta { get; set; }
|
|
public string Echo { get; set; }
|
|
public string Foxtrot { get; set; }
|
|
}
|
|
|
|
public class Customer
|
|
{
|
|
private Address _address = new Address();
|
|
|
|
public Address Address
|
|
{
|
|
get { return _address; }
|
|
}
|
|
}
|
|
|
|
public class Address
|
|
{
|
|
public string Street { get; set; }
|
|
public string Zip { get; set; }
|
|
}
|
|
|
|
public class IntegerContainer
|
|
{
|
|
public int Integer { get; set; }
|
|
public int? NullableInteger { get; set; }
|
|
}
|
|
|
|
[TypeConverter(typeof(CultureAwareConverter))]
|
|
public class StringContainer
|
|
{
|
|
public StringContainer(string value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public string Value { get; private set; }
|
|
}
|
|
|
|
private class CultureAwareConverter : TypeConverter
|
|
{
|
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
|
{
|
|
return (sourceType == typeof(string));
|
|
}
|
|
|
|
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
|
{
|
|
return (destinationType == typeof(string));
|
|
}
|
|
|
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
|
{
|
|
string stringValue = value as string;
|
|
if (stringValue == null || stringValue.Length < 3)
|
|
{
|
|
throw new Exception("Value must have at least 3 characters.");
|
|
}
|
|
return new StringContainer(AppendCultureName(stringValue, culture));
|
|
}
|
|
|
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
|
{
|
|
StringContainer container = value as StringContainer;
|
|
if (container.Value == null || container.Value.Length < 3)
|
|
{
|
|
throw new Exception("Value must have at least 3 characters.");
|
|
}
|
|
|
|
return AppendCultureName(container.Value, culture);
|
|
}
|
|
|
|
private static string AppendCultureName(string value, CultureInfo culture)
|
|
{
|
|
string cultureName = (!String.IsNullOrEmpty(culture.Name)) ? culture.Name : culture.ThreeLetterWindowsLanguageName;
|
|
return value + " (" + cultureName + ")";
|
|
}
|
|
}
|
|
|
|
[ModelBinder(typeof(MyStringModelBinder))]
|
|
private class MyStringModel
|
|
{
|
|
public string Value { get; set; }
|
|
}
|
|
|
|
private class MyStringModelBinder : IModelBinder
|
|
{
|
|
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
MyStringModel castModel = bindingContext.Model as MyStringModel;
|
|
if (castModel != null)
|
|
{
|
|
castModel.Value += "_Update";
|
|
}
|
|
else
|
|
{
|
|
castModel = new MyStringModel() { Value = bindingContext.ModelName + "_Create" };
|
|
}
|
|
return castModel;
|
|
}
|
|
}
|
|
|
|
private class CustomUnvalidatedValueProvider : IUnvalidatedValueProvider
|
|
{
|
|
public ValueProviderResult GetValue(string key, bool skipValidation)
|
|
{
|
|
string newValue = key + ((skipValidation) ? "Unvalidated" : "Validated");
|
|
return new ValueProviderResult(newValue, newValue, null);
|
|
}
|
|
|
|
public bool ContainsPrefix(string prefix)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public ValueProviderResult GetValue(string key)
|
|
{
|
|
return GetValue(key, skipValidation: false);
|
|
}
|
|
}
|
|
|
|
private class SimpleController : Controller
|
|
{
|
|
}
|
|
|
|
public class DefaultModelBinderHelper : DefaultModelBinder
|
|
{
|
|
public virtual void PublicBindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property)
|
|
{
|
|
base.BindProperty(controllerContext, bindingContext, property);
|
|
}
|
|
|
|
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property)
|
|
{
|
|
PublicBindProperty(controllerContext, bindingContext, property);
|
|
}
|
|
|
|
public virtual object PublicCreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
|
|
{
|
|
return base.CreateModel(controllerContext, bindingContext, modelType);
|
|
}
|
|
|
|
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
|
|
{
|
|
return PublicCreateModel(controllerContext, bindingContext, modelType);
|
|
}
|
|
|
|
public virtual IEnumerable<PropertyDescriptor> PublicGetFilteredModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
return base.GetFilteredModelProperties(controllerContext, bindingContext);
|
|
}
|
|
|
|
public virtual PropertyDescriptorCollection PublicGetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
return base.GetModelProperties(controllerContext, bindingContext);
|
|
}
|
|
|
|
protected override PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
return PublicGetModelProperties(controllerContext, bindingContext);
|
|
}
|
|
|
|
public string PublicCreateSubIndexName(string prefix, int indexName)
|
|
{
|
|
return CreateSubIndexName(prefix, indexName);
|
|
}
|
|
|
|
public string PublicCreateSubPropertyName(string prefix, string propertyName)
|
|
{
|
|
return CreateSubPropertyName(prefix, propertyName);
|
|
}
|
|
|
|
public static bool PublicIsModelValid(ModelBindingContext bindingContext)
|
|
{
|
|
return IsModelValid(bindingContext);
|
|
}
|
|
|
|
public virtual bool PublicOnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
return base.OnModelUpdating(controllerContext, bindingContext);
|
|
}
|
|
|
|
protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
return PublicOnModelUpdating(controllerContext, bindingContext);
|
|
}
|
|
|
|
public virtual void PublicOnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
base.OnModelUpdated(controllerContext, bindingContext);
|
|
}
|
|
|
|
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
|
{
|
|
PublicOnModelUpdated(controllerContext, bindingContext);
|
|
}
|
|
|
|
public virtual bool PublicOnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
return base.OnPropertyValidating(controllerContext, bindingContext, property, value);
|
|
}
|
|
|
|
protected override bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
return PublicOnPropertyValidating(controllerContext, bindingContext, property, value);
|
|
}
|
|
|
|
public virtual void PublicOnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
base.OnPropertyValidated(controllerContext, bindingContext, property, value);
|
|
}
|
|
|
|
protected override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
PublicOnPropertyValidated(controllerContext, bindingContext, property, value);
|
|
}
|
|
|
|
public virtual void PublicSetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
base.SetProperty(controllerContext, bindingContext, property, value);
|
|
}
|
|
|
|
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value)
|
|
{
|
|
PublicSetProperty(controllerContext, bindingContext, property, value);
|
|
}
|
|
}
|
|
|
|
private class MyModel2
|
|
{
|
|
private int _intReadWriteNonNegative;
|
|
|
|
public int IntReadOnly
|
|
{
|
|
get { return 4; }
|
|
}
|
|
|
|
public int IntReadWrite { get; set; }
|
|
|
|
public int IntReadWriteNonNegative
|
|
{
|
|
get { return _intReadWriteNonNegative; }
|
|
set
|
|
{
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException("value", "Value must be non-negative.");
|
|
}
|
|
_intReadWriteNonNegative = value;
|
|
}
|
|
}
|
|
|
|
public int? NullableIntReadWrite { get; set; }
|
|
}
|
|
|
|
[Bind(Exclude = "Foo")]
|
|
private class ModelWithBindAttribute : ModelWithoutBindAttribute
|
|
{
|
|
}
|
|
|
|
private class ModelWithoutBindAttribute
|
|
{
|
|
public string Foo { get; set; }
|
|
public string Bar { get; set; }
|
|
public string Baz { get; set; }
|
|
}
|
|
|
|
private class PropertyTestingModel
|
|
{
|
|
public string StringReadWrite { get; set; }
|
|
public string StringReadOnly { get; private set; }
|
|
public int IntReadWrite { get; set; }
|
|
public int IntReadOnly { get; private set; }
|
|
public object[] ArrayReadWrite { get; set; }
|
|
public object[] ArrayReadOnly { get; private set; }
|
|
public Address AddressReadWrite { get; set; }
|
|
public Address AddressReadOnly { get; private set; }
|
|
public string Whitelisted { get; set; }
|
|
public string Blacklisted { get; set; }
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// DataAnnotations tests
|
|
|
|
public const string BASE_MODEL_NAME = "BaseModelName";
|
|
|
|
// GetModelProperties tests
|
|
|
|
[MetadataType(typeof(Metadata))]
|
|
class GetModelPropertiesModel
|
|
{
|
|
[Required]
|
|
public int LocalAttributes { get; set; }
|
|
|
|
public int MetadataAttributes { get; set; }
|
|
|
|
[Required]
|
|
public int MixedAttributes { get; set; }
|
|
|
|
class Metadata
|
|
{
|
|
[Range(10, 100)]
|
|
public int MetadataAttributes { get; set; }
|
|
|
|
[Range(10, 100)]
|
|
public int MixedAttributes { get; set; }
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelPropertiesWithLocalAttributes()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetModelPropertiesModel> modelBinder = new TestableDefaultModelBinder<GetModelPropertiesModel>();
|
|
|
|
// Act
|
|
PropertyDescriptor property = modelBinder.GetModelProperties()
|
|
.Cast<PropertyDescriptor>()
|
|
.Where(pd => pd.Name == "LocalAttributes")
|
|
.Single();
|
|
|
|
// Assert
|
|
Assert.True(property.Attributes.Cast<Attribute>().Any(a => a is RequiredAttribute));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelPropertiesWithMetadataAttributes()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetModelPropertiesModel> modelBinder = new TestableDefaultModelBinder<GetModelPropertiesModel>();
|
|
|
|
// Act
|
|
PropertyDescriptor property = modelBinder.GetModelProperties()
|
|
.Cast<PropertyDescriptor>()
|
|
.Where(pd => pd.Name == "MetadataAttributes")
|
|
.Single();
|
|
|
|
// Assert
|
|
Assert.True(property.Attributes.Cast<Attribute>().Any(a => a is RangeAttribute));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetModelPropertiesWithMixedAttributes()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetModelPropertiesModel> modelBinder = new TestableDefaultModelBinder<GetModelPropertiesModel>();
|
|
|
|
// Act
|
|
PropertyDescriptor property = modelBinder.GetModelProperties()
|
|
.Cast<PropertyDescriptor>()
|
|
.Where(pd => pd.Name == "MixedAttributes")
|
|
.Single();
|
|
|
|
// Assert
|
|
Assert.True(property.Attributes.Cast<Attribute>().Any(a => a is RequiredAttribute));
|
|
Assert.True(property.Attributes.Cast<Attribute>().Any(a => a is RangeAttribute));
|
|
}
|
|
|
|
// GetPropertyValue tests
|
|
|
|
class GetPropertyValueModel
|
|
{
|
|
public string NoAttribute { get; set; }
|
|
|
|
[DisplayFormat(ConvertEmptyStringToNull = false)]
|
|
public string AttributeWithoutConversion { get; set; }
|
|
|
|
[DisplayFormat(ConvertEmptyStringToNull = true)]
|
|
public string AttributeWithConversion { get; set; }
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValueWithNoAttributeConvertsEmptyStringToNull()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetPropertyValueModel> binder = new TestableDefaultModelBinder<GetPropertyValueModel>();
|
|
binder.Context.ModelMetadata = binder.Context.PropertyMetadata["NoAttribute"];
|
|
|
|
// Act
|
|
object result = binder.GetPropertyValue("NoAttribute", String.Empty);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValueWithFalseAttributeDoesNotConvertEmptyStringToNull()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetPropertyValueModel> binder = new TestableDefaultModelBinder<GetPropertyValueModel>();
|
|
binder.Context.ModelMetadata = binder.Context.PropertyMetadata["AttributeWithoutConversion"];
|
|
|
|
// Act
|
|
object result = binder.GetPropertyValue("AttributeWithoutConversion", String.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(String.Empty, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPropertyValueWithTrueAttributeConvertsEmptyStringToNull()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<GetPropertyValueModel> binder = new TestableDefaultModelBinder<GetPropertyValueModel>();
|
|
binder.Context.ModelMetadata = binder.Context.PropertyMetadata["AttributeWithConversion"];
|
|
|
|
// Act
|
|
object result = binder.GetPropertyValue("AttributeWithConversion", String.Empty);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
// OnModelUpdated tests
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedPassesNullContainerToValidate()
|
|
{
|
|
Mock<ModelValidatorProvider> provider = null;
|
|
|
|
try
|
|
{
|
|
// Arrange
|
|
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(object));
|
|
ControllerContext context = new ControllerContext();
|
|
Mock<ModelValidator> validator = new Mock<ModelValidator>(metadata, context);
|
|
provider = new Mock<ModelValidatorProvider>();
|
|
provider.Setup(p => p.GetValidators(It.IsAny<ModelMetadata>(), It.IsAny<ControllerContext>()))
|
|
.Returns(new ModelValidator[] { validator.Object });
|
|
ModelValidatorProviders.Providers.Add(provider.Object);
|
|
object model = new object();
|
|
TestableDefaultModelBinder<object> modelBinder = new TestableDefaultModelBinder<object>(model);
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
validator.Verify(v => v.Validate(null));
|
|
}
|
|
finally
|
|
{
|
|
if (provider != null)
|
|
{
|
|
ModelValidatorProviders.Providers.Remove(provider.Object);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class MinMaxValidationAttribute : ValidationAttribute
|
|
{
|
|
public MinMaxValidationAttribute()
|
|
: base("Minimum must be less than or equal to Maximum")
|
|
{
|
|
}
|
|
|
|
public override bool IsValid(object value)
|
|
{
|
|
OnModelUpdatedModelMultipleParameters model = (OnModelUpdatedModelMultipleParameters)value;
|
|
return model.Minimum <= model.Maximum;
|
|
}
|
|
}
|
|
|
|
[MinMaxValidation]
|
|
public class OnModelUpdatedModelMultipleParameters
|
|
{
|
|
public int Minimum { get; set; }
|
|
public int Maximum { get; set; }
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedWithValidationAttributeMultipleParameters()
|
|
{
|
|
// Arrange
|
|
OnModelUpdatedModelMultipleParameters model = new OnModelUpdatedModelMultipleParameters { Minimum = 250, Maximum = 100 };
|
|
TestableDefaultModelBinder<OnModelUpdatedModelMultipleParameters> modelBinder = new TestableDefaultModelBinder<OnModelUpdatedModelMultipleParameters>(model);
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
Assert.Single(modelBinder.ModelState);
|
|
ModelState stateModel = modelBinder.ModelState[BASE_MODEL_NAME];
|
|
Assert.NotNull(stateModel);
|
|
Assert.Equal("Minimum must be less than or equal to Maximum", stateModel.Errors.Single().ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedWithInvalidPropertyValidationWillNotRunEntityLevelValidation()
|
|
{
|
|
// Arrange
|
|
OnModelUpdatedModelMultipleParameters model = new OnModelUpdatedModelMultipleParameters { Minimum = 250, Maximum = 100 };
|
|
TestableDefaultModelBinder<OnModelUpdatedModelMultipleParameters> modelBinder = new TestableDefaultModelBinder<OnModelUpdatedModelMultipleParameters>(model);
|
|
modelBinder.ModelState.AddModelError(BASE_MODEL_NAME + ".Minimum", "The minimum value was invalid.");
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
Assert.Null(modelBinder.ModelState[BASE_MODEL_NAME]);
|
|
}
|
|
|
|
public class AlwaysInvalidAttribute : ValidationAttribute
|
|
{
|
|
public AlwaysInvalidAttribute()
|
|
{
|
|
}
|
|
|
|
public AlwaysInvalidAttribute(string message)
|
|
: base(message)
|
|
{
|
|
}
|
|
|
|
public override bool IsValid(object value)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[AlwaysInvalid("The object just isn't right")]
|
|
public class OnModelUpdatedModelNoParameters
|
|
{
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedWithValidationAttributeNoParameters()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnModelUpdatedModelNoParameters> modelBinder = new TestableDefaultModelBinder<OnModelUpdatedModelNoParameters>();
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
Assert.Single(modelBinder.ModelState);
|
|
ModelState stateModel = modelBinder.ModelState[BASE_MODEL_NAME];
|
|
Assert.NotNull(stateModel);
|
|
Assert.Equal("The object just isn't right", stateModel.Errors.Single().ErrorMessage);
|
|
}
|
|
|
|
[AlwaysInvalid]
|
|
public class OnModelUpdatedModelNoValidationResult
|
|
{
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedWithValidationAttributeNoValidationMessage()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnModelUpdatedModelNoValidationResult> modelBinder = new TestableDefaultModelBinder<OnModelUpdatedModelNoValidationResult>();
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
Assert.Single(modelBinder.ModelState);
|
|
ModelState stateModel = modelBinder.ModelState[BASE_MODEL_NAME];
|
|
Assert.NotNull(stateModel);
|
|
Assert.Equal("The field OnModelUpdatedModelNoValidationResult is invalid.", stateModel.Errors.Single().ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnModelUpdatedDoesNotPlaceErrorMessagesInModelStateWhenSubPropertiesHaveErrors()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnModelUpdatedModelNoValidationResult> modelBinder = new TestableDefaultModelBinder<OnModelUpdatedModelNoValidationResult>();
|
|
modelBinder.ModelState.AddModelError("Foo.Bar", "Foo.Bar is invalid");
|
|
modelBinder.Context.ModelName = "Foo";
|
|
|
|
// Act
|
|
modelBinder.OnModelUpdated();
|
|
|
|
// Assert
|
|
Assert.Null(modelBinder.ModelState["Foo"]);
|
|
}
|
|
|
|
// OnPropertyValidating tests
|
|
|
|
class OnPropertyValidatingModel
|
|
{
|
|
public string NotValidated { get; set; }
|
|
|
|
[Range(10, 65)]
|
|
public int RangedInteger { get; set; }
|
|
|
|
[Required(ErrorMessage = "Custom Required Message")]
|
|
public int RequiredInteger { get; set; }
|
|
}
|
|
|
|
[Fact]
|
|
public void OnPropertyValidatingWithoutValidationAttribute()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnPropertyValidatingModel> modelBinder = new TestableDefaultModelBinder<OnPropertyValidatingModel>();
|
|
|
|
// Act
|
|
modelBinder.OnPropertyValidating("NotValidated", 42);
|
|
|
|
// Assert
|
|
Assert.Empty(modelBinder.ModelState);
|
|
}
|
|
|
|
[Fact]
|
|
public void OnPropertyValidatingWithValidationAttributePassing()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnPropertyValidatingModel> modelBinder = new TestableDefaultModelBinder<OnPropertyValidatingModel>();
|
|
modelBinder.Context.PropertyMetadata["RangedInteger"].Model = 42;
|
|
|
|
// Act
|
|
bool result = modelBinder.OnPropertyValidating("RangedInteger", 42);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
Assert.Empty(modelBinder.ModelState);
|
|
}
|
|
|
|
// SetProperty tests
|
|
|
|
[Fact]
|
|
public void SetPropertyWithRequiredOnValueTypeOnlyResultsInSingleMessage()
|
|
{ // DDB #225150
|
|
// Arrange
|
|
TestableDefaultModelBinder<OnPropertyValidatingModel> modelBinder = new TestableDefaultModelBinder<OnPropertyValidatingModel>();
|
|
modelBinder.Context.ModelMetadata.Model = new OnPropertyValidatingModel();
|
|
|
|
// Act
|
|
modelBinder.SetProperty("RequiredInteger", null);
|
|
|
|
// Assert
|
|
ModelState modelState = modelBinder.ModelState[BASE_MODEL_NAME + ".RequiredInteger"];
|
|
ModelError modelStateError = modelState.Errors.Single();
|
|
Assert.Equal("Custom Required Message", modelStateError.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertyAddsDefaultMessageForNonBindableNonNullableValueTypes()
|
|
{
|
|
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
|
|
|
|
try
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<List<String>> modelBinder = new TestableDefaultModelBinder<List<String>>();
|
|
modelBinder.Context.ModelMetadata.Model = null;
|
|
|
|
// Act
|
|
modelBinder.SetProperty("Count", null);
|
|
|
|
// Assert
|
|
ModelState modelState = modelBinder.ModelState[BASE_MODEL_NAME + ".Count"];
|
|
ModelError modelStateError = modelState.Errors.Single();
|
|
Assert.Equal("A value is required.", modelStateError.ErrorMessage);
|
|
}
|
|
finally
|
|
{
|
|
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = true;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertyCreatesValueRequiredErrorIfNecessary()
|
|
{
|
|
// Arrange
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new MyModel(), typeof(MyModel)),
|
|
ModelName = "theModel",
|
|
};
|
|
|
|
PropertyDescriptor property = TypeDescriptor.GetProperties(typeof(MyModel))["ReadWriteProperty"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
helper.PublicSetProperty(new ControllerContext(), bindingContext, property, null);
|
|
|
|
// Assert
|
|
Assert.Equal("The ReadWriteProperty field is required.", bindingContext.ModelState["theModel.ReadWriteProperty"].Errors[0].ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertyWithThrowingSetter()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<SetPropertyModel> binder = new TestableDefaultModelBinder<SetPropertyModel>();
|
|
|
|
// Act
|
|
binder.SetProperty("NonNullableString", null);
|
|
|
|
// Assert
|
|
ModelState modelState = binder.Context.ModelState[BASE_MODEL_NAME + ".NonNullableString"];
|
|
Assert.Single(modelState.Errors);
|
|
Assert.IsType<ArgumentNullException>(modelState.Errors[0].Exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertyWithNullValueAndThrowingSetterWithRequiredAttribute()
|
|
{ // DDB #227809
|
|
// Arrange
|
|
TestableDefaultModelBinder<SetPropertyModel> binder = new TestableDefaultModelBinder<SetPropertyModel>();
|
|
|
|
// Act
|
|
binder.SetProperty("NonNullableStringWithAttribute", null);
|
|
|
|
// Assert
|
|
ModelState modelState = binder.Context.ModelState[BASE_MODEL_NAME + ".NonNullableStringWithAttribute"];
|
|
var error = Assert.Single(modelState.Errors);
|
|
Assert.Equal("My custom required message", error.ErrorMessage);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertyDoesNothingIfPropertyIsReadOnly()
|
|
{
|
|
// Arrange
|
|
MyModel model = new MyModel();
|
|
ModelBindingContext bindingContext = new ModelBindingContext()
|
|
{
|
|
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()),
|
|
ModelName = "theModel"
|
|
};
|
|
|
|
PropertyDescriptor property = TypeDescriptor.GetProperties(model)["ReadOnlyProperty"];
|
|
DefaultModelBinderHelper helper = new DefaultModelBinderHelper();
|
|
|
|
// Act
|
|
helper.PublicSetProperty(new ControllerContext(), bindingContext, property, 42);
|
|
|
|
// Assert
|
|
Assert.Empty(bindingContext.ModelState);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetPropertySuccess()
|
|
{
|
|
// Arrange
|
|
TestableDefaultModelBinder<SetPropertyModel> binder = new TestableDefaultModelBinder<SetPropertyModel>();
|
|
|
|
// Act
|
|
binder.SetProperty("NullableString", "The new value");
|
|
|
|
// Assert
|
|
Assert.Equal("The new value", ((SetPropertyModel)binder.Context.Model).NullableString);
|
|
Assert.Empty(binder.Context.ModelState);
|
|
}
|
|
|
|
class SetPropertyModel
|
|
{
|
|
public string NullableString { get; set; }
|
|
|
|
public string NonNullableString
|
|
{
|
|
get { return null; }
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
}
|
|
}
|
|
|
|
[Required(ErrorMessage = "My custom required message")]
|
|
public string NonNullableStringWithAttribute
|
|
{
|
|
get { return null; }
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
static PropertyDescriptor GetProperty<T>(string propertyName)
|
|
{
|
|
return TypeDescriptor.GetProperties(typeof(T))
|
|
.Cast<PropertyDescriptor>()
|
|
.Where(p => p.Name == propertyName)
|
|
.Single();
|
|
}
|
|
|
|
static ICustomTypeDescriptor GetType<T>()
|
|
{
|
|
return TypeDescriptor.GetProvider(typeof(T)).GetTypeDescriptor(typeof(T));
|
|
}
|
|
|
|
// Helper classes
|
|
|
|
class TestableDefaultModelBinder<TModel> : DefaultModelBinder
|
|
where TModel : new()
|
|
{
|
|
public TestableDefaultModelBinder()
|
|
: this(new TModel())
|
|
{
|
|
}
|
|
|
|
public TestableDefaultModelBinder(TModel model)
|
|
{
|
|
ModelState = new ModelStateDictionary();
|
|
|
|
Context = new ModelBindingContext();
|
|
Context.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(TModel));
|
|
Context.ModelName = BASE_MODEL_NAME;
|
|
Context.ModelState = ModelState;
|
|
}
|
|
|
|
public ModelBindingContext Context { get; private set; }
|
|
|
|
public ModelStateDictionary ModelState { get; set; }
|
|
|
|
public void BindProperty(string propertyName)
|
|
{
|
|
base.BindProperty(new ControllerContext(), Context, GetProperty<TModel>(propertyName));
|
|
}
|
|
|
|
public PropertyDescriptorCollection GetModelProperties()
|
|
{
|
|
return base.GetModelProperties(new ControllerContext(), Context);
|
|
}
|
|
|
|
public object GetPropertyValue(string propertyName, object existingValue)
|
|
{
|
|
Mock<IModelBinder> mockModelBinder = new Mock<IModelBinder>();
|
|
mockModelBinder.Setup(b => b.BindModel(It.IsAny<ControllerContext>(), Context)).Returns(existingValue);
|
|
return base.GetPropertyValue(new ControllerContext(), Context, GetProperty<TModel>(propertyName), mockModelBinder.Object);
|
|
}
|
|
|
|
public bool OnPropertyValidating(string propertyName, object value)
|
|
{
|
|
return base.OnPropertyValidating(new ControllerContext(), Context, GetProperty<TModel>(propertyName), value);
|
|
}
|
|
|
|
public void OnPropertyValidated(string propertyName, object value)
|
|
{
|
|
base.OnPropertyValidated(new ControllerContext(), Context, GetProperty<TModel>(propertyName), value);
|
|
}
|
|
|
|
public void OnModelUpdated()
|
|
{
|
|
base.OnModelUpdated(new ControllerContext(), Context);
|
|
}
|
|
|
|
public void SetProperty(string propertyName, object value)
|
|
{
|
|
base.SetProperty(new ControllerContext(), Context, GetProperty<TModel>(propertyName), value);
|
|
}
|
|
}
|
|
|
|
private class CountryState
|
|
{
|
|
public string Name { get; set; }
|
|
|
|
public IEnumerable<string> States { get; set; }
|
|
}
|
|
}
|
|
}
|