diff --git a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj index 16c25e8b50..0291a37558 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj +++ b/src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj @@ -147,6 +147,7 @@ + diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index 5e2fa586b9..0604ae95aa 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Mvc.Core; +using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ReflectedModelBuilder; namespace Microsoft.AspNet.Mvc @@ -15,6 +16,14 @@ namespace Microsoft.AspNet.Mvc public MvcOptions() { ApplicationModelConventions = new List(); + ModelBinders = new List + { + new ModelBinderDescriptor(new TypeConverterModelBinder()), + new ModelBinderDescriptor(new TypeMatchModelBinder()), + new ModelBinderDescriptor(typeof(GenericModelBinder)), + new ModelBinderDescriptor(new MutableObjectModelBinder()), + new ModelBinderDescriptor(new ComplexModelDtoModelBinder()), + }; } public AntiForgeryOptions AntiForgeryOptions @@ -28,7 +37,7 @@ namespace Microsoft.AspNet.Mvc { if (value == null) { - throw new ArgumentNullException("value", + throw new ArgumentNullException("value", Resources.FormatPropertyOfTypeCannotBeNull("AntiForgeryOptions", typeof(MvcOptions))); } @@ -37,6 +46,8 @@ namespace Microsoft.AspNet.Mvc } } + public List ModelBinders { get; private set; } + public List ApplicationModelConventions { get; private set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs index d0ec65d500..cfea960128 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs @@ -6,29 +6,26 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Routing; namespace Microsoft.AspNet.Mvc { public class DefaultActionBindingContextProvider : IActionBindingContextProvider { private readonly IModelMetadataProvider _modelMetadataProvider; - private readonly IEnumerable _modelBinders; + private readonly ICompositeModelBinder _compositeModelBinder; private readonly IEnumerable _valueProviderFactories; private readonly IInputFormatterProvider _inputFormatterProvider; private readonly IEnumerable _validatorProviders; - private Tuple _bindingContext; public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider, - IEnumerable modelBinders, + ICompositeModelBinder compositeModelBinder, IEnumerable valueProviderFactories, IInputFormatterProvider inputFormatterProvider, IEnumerable validatorProviders) { _modelMetadataProvider = modelMetadataProvider; - _modelBinders = modelBinders.OrderBy( - binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0).ToArray(); + _compositeModelBinder = compositeModelBinder; _valueProviderFactories = valueProviderFactories; _inputFormatterProvider = inputFormatterProvider; _validatorProviders = validatorProviders; @@ -54,7 +51,7 @@ namespace Microsoft.AspNet.Mvc var context = new ActionBindingContext( actionContext, _modelMetadataProvider, - new CompositeModelBinder(_modelBinders), + _compositeModelBinder, new CompositeValueProvider(valueProviders), _inputFormatterProvider, _validatorProviders); diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultModelBindersProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultModelBindersProvider.cs new file mode 100644 index 0000000000..d5bb068a60 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultModelBindersProvider.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.OptionsModel; + +namespace Microsoft.AspNet.Mvc +{ + /// + public class DefaultModelBindersProvider : IModelBindersProvider + { + private readonly List _descriptors; + private readonly ITypeActivator _typeActivator; + private readonly IServiceProvider _serviceProvider; + + /// + /// Initializes a new instance of the DefaultModelBindersProvider class. + /// + /// An accessor to the configured for this application. + /// An instance used to instantiate types. + /// A instance that retrieves services from the + /// service collection. + public DefaultModelBindersProvider(IOptionsAccessor options, + ITypeActivator typeActivator, + IServiceProvider serviceProvider) + { + _descriptors = options.Options.ModelBinders; + _typeActivator = typeActivator; + _serviceProvider = serviceProvider; + } + + /// + public IReadOnlyList ModelBinders + { + get + { + var binders = new List(); + foreach (var descriptor in _descriptors) + { + var binder = descriptor.ModelBinder; + if (binder == null) + { + binder = (IModelBinder)_typeActivator.CreateInstance(_serviceProvider, + descriptor.ModelBinderType); + } + + binders.Add(binder); + } + return binders; + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs index 5c5bd02180..206e5f6ae2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs @@ -2,33 +2,45 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace Microsoft.AspNet.Mvc.ModelBinding { /// - /// This class is an that delegates to one of a collection of - /// instances. + /// Represents an that delegates to one of a collection of + /// instances. /// /// /// If no binder is available and the allows it, /// this class tries to find a binder using an empty prefix. /// - public class CompositeModelBinder : IModelBinder + public class CompositeModelBinder : ICompositeModelBinder { - public CompositeModelBinder([NotNull] IEnumerable binders) - : this(binders.ToArray()) + private readonly IModelBindersProvider _modelBindersProvider; + private IReadOnlyList _binders; + + /// + /// Initializes a new instance of the CompositeModelBinder class. + /// + /// Provides a collection of instances. + public CompositeModelBinder(IModelBindersProvider modelBindersProvider) { + _modelBindersProvider = modelBindersProvider; } - public CompositeModelBinder(params IModelBinder[] binders) + /// + public IReadOnlyList ModelBinders { - Binders = binders; + get + { + if (_binders == null) + { + _binders = _modelBindersProvider.ModelBinders; + } + return _binders; + } } - private IModelBinder[] Binders { get; set; } - public virtual async Task BindModelAsync([NotNull] ModelBindingContext bindingContext) { var newBindingContext = CreateNewBindingContext(bindingContext, @@ -86,7 +98,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Protects against stack overflow for deeply nested model binding // RuntimeHelpers.EnsureSufficientExecutionStack(); - foreach (var binder in Binders) + foreach (var binder in ModelBinders) { if (await binder.BindModelAsync(bindingContext)) { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ICompositeModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ICompositeModelBinder.cs new file mode 100644 index 0000000000..8e7498a282 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ICompositeModelBinder.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Represents an aggregate of that delegates to one of the instances for model binding. + /// + public interface ICompositeModelBinder : IModelBinder + { + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IModelBindersProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IModelBindersProvider.cs new file mode 100644 index 0000000000..d9d4faf172 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IModelBindersProvider.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Provides an activated collection of instances. + /// + public interface IModelBindersProvider + { + /// + /// Gets a collection of activated ModelBinders instances. + /// + IReadOnlyList ModelBinders { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptor.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptor.cs new file mode 100644 index 0000000000..e6b75cab8a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptor.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Encapsulates information that describes an . + /// + public class ModelBinderDescriptor + { + /// + /// Creates a new instance of . + /// + /// A + /// Creates a new instance of . + /// + /// An instance of that the descriptor represents. + public ModelBinderDescriptor([NotNull] IModelBinder modelBinder) + { + ModelBinder = modelBinder; + ModelBinderType = modelBinder.GetType(); + } + + /// + /// Gets the type of the . + /// + public Type ModelBinderType + { + get; + private set; + } + + /// + /// Gets the instance of the . + /// + public IModelBinder ModelBinder + { + get; + private set; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptorExtensions.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptorExtensions.cs new file mode 100644 index 0000000000..8fe9a02883 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/ModelBinderDescriptorExtensions.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + /// + /// Extension methods for adding model binders to a collection. + /// + public static class ModelBinderDescriptorExtensions + { + /// + /// Adds a type representing a to a descriptor collection. + /// + /// A list of ModelBinderDescriptors + /// Type representing an . + /// ModelBinderDescriptor representing the added instance. + public static ModelBinderDescriptor Add([NotNull] this IList descriptors, + [NotNull] Type modelBinderType) + { + var descriptor = new ModelBinderDescriptor(modelBinderType); + descriptors.Add(descriptor); + return descriptor; + } + + /// + /// Inserts a type representing a to a descriptor collection. + /// + /// A list of ModelBinderDescriptors + /// Type representing an . + /// ModelBinderDescriptor representing the inserted instance. + public static ModelBinderDescriptor Insert([NotNull] this IList descriptors, + int index, + [NotNull] Type modelBinderType) + { + if (index < 0 || index > descriptors.Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + var descriptor = new ModelBinderDescriptor(modelBinderType); + descriptors.Insert(index, descriptor); + return descriptor; + } + + /// + /// Adds an to a descriptor collection. + /// + /// A list of ModelBinderDescriptors + /// An instance. + /// ModelBinderDescriptor representing the added instance. + public static ModelBinderDescriptor Add([NotNull] this IList descriptors, + [NotNull] IModelBinder modelBinder) + { + var descriptor = new ModelBinderDescriptor(modelBinder); + descriptors.Add(descriptor); + return descriptor; + } + + /// + /// Insert an to a descriptor collection. + /// + /// A list of ModelBinderDescriptors + /// An instance. + /// ModelBinderDescriptor representing the added instance. + public static ModelBinderDescriptor Insert([NotNull] this IList descriptors, + int index, + [NotNull] IModelBinder modelBinder) + { + if (index < 0 || index > descriptors.Count) + { + throw new ArgumentOutOfRangeException("index"); + } + + var descriptor = new ModelBinderDescriptor(modelBinder); + descriptors.Insert(index, descriptor); + return descriptor; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj b/src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj index c2dc72ddf5..b029128842 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj @@ -33,8 +33,12 @@ + + + + diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 4c54d12bd5..e7e937e9a7 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc ReflectedActionDescriptorProvider>(); yield return describe.Transient, ReflectedActionInvokerProvider>(); - yield return describe.Singleton(); yield return describe.Transient(); @@ -54,15 +54,12 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); - yield return describe.Transient(); yield return describe.Transient(); + yield return describe.Transient(); + yield return describe.Transient(); + yield return describe.Transient, DefaultFilterProvider>(); yield return describe.Transient(); @@ -72,15 +69,15 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); yield return describe.Transient(); - yield return describe.Transient, + yield return describe.Transient, DefaultViewComponentInvokerProvider>(); yield return describe.Transient(); yield return describe.Transient(); yield return describe.Singleton(); yield return describe.Singleton(); - yield return describe.Singleton(); + yield return describe.Singleton(); yield return describe.Describe( diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj index 705ded6643..ace2c4d398 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj @@ -55,6 +55,7 @@ + diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/DefaultModelBindersProviderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/DefaultModelBindersProviderTest.cs new file mode 100644 index 0000000000..e514c8e9b9 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ParameterBinding/DefaultModelBindersProviderTest.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 +using System; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.OptionsModel; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Core +{ + public class DefaultModelBindersProviderTest + { + [Fact] + public void DefaultModelBindersProvider_ProvidesInstancesOfModelBinders() + { + // Arrange + var binder = new TypeMatchModelBinder(); + var options = new MvcOptions(); + options.ModelBinders.Clear(); + options.ModelBinders.Add(binder); + options.ModelBinders.Add(typeof(GenericModelBinder)); + var optionsAccessor = new Mock>(); + optionsAccessor.SetupGet(o => o.Options) + .Returns(options); + var activator = new Mock(); + var serviceProvider = Mock.Of(); + activator.Setup(a => a.CreateInstance(serviceProvider, typeof(GenericModelBinder))) + .Returns(new GenericModelBinder(serviceProvider, activator.Object)) + .Verifiable(); + + var provider = new DefaultModelBindersProvider(optionsAccessor.Object, activator.Object, serviceProvider); + + // Act + var binders = provider.ModelBinders; + + // Assert + Assert.Equal(2, binders.Count); + Assert.Same(binder, binders[0]); + Assert.IsType(binders[1]); + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs index cf72d1a2d5..3afa72fdae 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/CompositeModelBinderTest.cs @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test mockIntBinder .Setup(o => o.BindModelAsync(It.IsAny())) .Returns( - delegate(ModelBindingContext context) + delegate (ModelBindingContext context) { Assert.Same(bindingContext.ModelMetadata, context.ModelMetadata); Assert.Equal("someName", context.ModelName); @@ -48,8 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test bindingContext.ValidationNode.Validating += delegate { validationCalled = true; }; return Task.FromResult(true); }); - - var shimBinder = new CompositeModelBinder(mockIntBinder.Object); + var shimBinder = CreateCompositeBinder(mockIntBinder.Object); // Act var isBound = await shimBinder.BindModelAsync(bindingContext); @@ -59,7 +58,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test Assert.Equal(42, bindingContext.Model); Assert.True(validationCalled); - Assert.Equal(true, bindingContext.ModelState.IsValid); + Assert.True(bindingContext.ModelState.IsValid); } [Fact] @@ -102,7 +101,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test return Task.FromResult(true); }); - IModelBinder shimBinder = new CompositeModelBinder(mockIntBinder.Object); + var shimBinder = CreateCompositeBinder(mockIntBinder.Object); // Act var isBound = await shimBinder.BindModelAsync(bindingContext); @@ -146,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test { // Arrange var innerBinder = Mock.Of(); - var shimBinder = new CompositeModelBinder(innerBinder); + var shimBinder = CreateCompositeBinder(innerBinder); var bindingContext = new ModelBindingContext { @@ -300,10 +299,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test new TypeConverterModelBinder(), new MutableObjectModelBinder() }; - var binder = new CompositeModelBinder(binders); + var binderProviders = new Mock(); + binderProviders.SetupGet(p => p.ModelBinders) + .Returns(binders); + var binder = new CompositeModelBinder(binderProviders.Object); return binder; } + private static CompositeModelBinder CreateCompositeBinder(IModelBinder mockIntBinder) + { + var binderProvider = new Mock(); + binderProvider.SetupGet(p => p.ModelBinders) + .Returns(new[] { mockIntBinder }); + + var shimBinder = new CompositeModelBinder(binderProvider.Object); + return shimBinder; + } + private class SimplePropertiesModel { public string FirstName { get; set; } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs index 111001da07..c3f8f9087b 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/KeyValuePairModelBinderTest.cs @@ -50,7 +50,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test public async Task BindModel_SubBindingSucceeds() { // Arrange - var innerBinder = new CompositeModelBinder(CreateStringBinder(), CreateIntBinder()); + var binderProviders = new Mock(); + binderProviders.SetupGet(b => b.ModelBinders) + .Returns(new[] { CreateStringBinder(), CreateIntBinder() }); + var innerBinder = new CompositeModelBinder(binderProviders.Object); var valueProvider = new SimpleHttpValueProvider(); var bindingContext = GetBindingContext(valueProvider, innerBinder); diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorExtensionsTest.cs new file mode 100644 index 0000000000..6b59a94831 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorExtensionsTest.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#if NET45 +using System; +using System.Collections.Generic; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class ModelBinderDescriptorExtensionTest + { + [Theory] + [InlineData(-1)] + [InlineData(5)] + public void Insert_WithType_ThrowsIfIndexIsOutOfBounds(int index) + { + // Arrange + var collection = new List + { + new ModelBinderDescriptor(Mock.Of()), + new ModelBinderDescriptor(Mock.Of()) + }; + + // Act & Assert + Assert.Throws("index", () => collection.Insert(index, typeof(IModelBinder))); + } + + [Theory] + [InlineData(-2)] + [InlineData(3)] + public void Insert_WithInstance_ThrowsIfIndexIsOutOfBounds(int index) + { + // Arrange + var collection = new List + { + new ModelBinderDescriptor(Mock.Of()), + new ModelBinderDescriptor(Mock.Of()) + }; + var binder = Mock.Of(); + + // Act & Assert + Assert.Throws("index", () => collection.Insert(index, binder)); + } + + [InlineData] + public void ModelBinderDescriptors_AddsTypesAndInstances() + { + // Arrange + var binder1 = Mock.Of(); + var binder2 = Mock.Of(); + var type1 = typeof(TypeMatchModelBinder); + var type2 = typeof(TypeConverterModelBinder); + var collection = new List(); + + // Act + collection.Add(binder1); + collection.Insert(1, binder2); + collection.Add(type1); + collection.Insert(2, type2); + + // Assert + Assert.Equal(4, collection.Count); + Assert.Equal(binder1, collection[0].ModelBinder); + Assert.Equal(binder2, collection[1].ModelBinder); + Assert.Equal(type2, collection[2].ModelBinderType); + Assert.Equal(type1, collection[3].ModelBinderType); + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorTest.cs new file mode 100644 index 0000000000..e3e5f3ce25 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Binders/ModelBinderDescriptorTest.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Testing; +using Xunit; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class ModelBinderDescriptorTest + { + [Fact] + public void ConstructorThrows_IfTypeIsNotIModelBinder() + { + // Arrange + var expected = "The type 'System.String' must derive from " + + "'Microsoft.AspNet.Mvc.ModelBinding.IModelBinder'."; + var type = typeof(string); + + // Act & Assert + ExceptionAssert.ThrowsArgument(() => new ModelBinderDescriptor(type), "modelBinderType", expected); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj index c40daad21f..2851854fe0 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj @@ -29,6 +29,8 @@ + +