Moving model binders from DI to MvcOptions

This commit is contained in:
Pranav K 2014-06-18 12:26:51 -07:00
parent ba506b2976
commit 3c092cb083
18 changed files with 447 additions and 37 deletions

View File

@ -147,6 +147,7 @@
<Compile Include="ParameterBindingInfo.cs" />
<Compile Include="ParameterBinding\ActionBindingContext.cs" />
<Compile Include="ParameterBinding\DefaultActionBindingContextProvider.cs" />
<Compile Include="ParameterBinding\DefaultModelBindersProvider.cs" />
<Compile Include="ParameterBinding\FromBodyAttribute.cs" />
<Compile Include="ParameterBinding\IActionBindingContextProvider.cs" />
<Compile Include="ParameterDescriptor.cs" />

View File

@ -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<IReflectedApplicationModelConvention>();
ModelBinders = new List<ModelBinderDescriptor>
{
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<ModelBinderDescriptor> ModelBinders { get; private set; }
public List<IReflectedApplicationModelConvention> ApplicationModelConventions { get; private set; }
}
}

View File

@ -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<IModelBinder> _modelBinders;
private readonly ICompositeModelBinder _compositeModelBinder;
private readonly IEnumerable<IValueProviderFactory> _valueProviderFactories;
private readonly IInputFormatterProvider _inputFormatterProvider;
private readonly IEnumerable<IModelValidatorProvider> _validatorProviders;
private Tuple<ActionContext, ActionBindingContext> _bindingContext;
public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider,
IEnumerable<IModelBinder> modelBinders,
ICompositeModelBinder compositeModelBinder,
IEnumerable<IValueProviderFactory> valueProviderFactories,
IInputFormatterProvider inputFormatterProvider,
IEnumerable<IModelValidatorProvider> 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);

View File

@ -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
{
/// <inheritdoc />
public class DefaultModelBindersProvider : IModelBindersProvider
{
private readonly List<ModelBinderDescriptor> _descriptors;
private readonly ITypeActivator _typeActivator;
private readonly IServiceProvider _serviceProvider;
/// <summary>
/// Initializes a new instance of the DefaultModelBindersProvider class.
/// </summary>
/// <param name="options">An accessor to the <see cref="MvcOptions"/> configured for this application.</param>
/// <param name="typeActivator">An <see cref="ITypeActivator"/> instance used to instantiate types.</param>
/// <param name="serviceProvider">A <see cref="IServiceProvider"/> instance that retrieves services from the
/// service collection.</param>
public DefaultModelBindersProvider(IOptionsAccessor<MvcOptions> options,
ITypeActivator typeActivator,
IServiceProvider serviceProvider)
{
_descriptors = options.Options.ModelBinders;
_typeActivator = typeActivator;
_serviceProvider = serviceProvider;
}
/// <inheritdoc />
public IReadOnlyList<IModelBinder> ModelBinders
{
get
{
var binders = new List<IModelBinder>();
foreach (var descriptor in _descriptors)
{
var binder = descriptor.ModelBinder;
if (binder == null)
{
binder = (IModelBinder)_typeActivator.CreateInstance(_serviceProvider,
descriptor.ModelBinderType);
}
binders.Add(binder);
}
return binders;
}
}
}
}

View File

@ -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
{
/// <summary>
/// This class is an <see cref="IModelBinder"/> that delegates to one of a collection of
/// <see cref="IModelBinder"/> instances.
/// Represents an <see cref="IModelBinder"/> that delegates to one of a collection of <see cref="IModelBinder"/>
/// instances.
/// </summary>
/// <remarks>
/// If no binder is available and the <see cref="ModelBindingContext"/> allows it,
/// this class tries to find a binder using an empty prefix.
/// </remarks>
public class CompositeModelBinder : IModelBinder
public class CompositeModelBinder : ICompositeModelBinder
{
public CompositeModelBinder([NotNull] IEnumerable<IModelBinder> binders)
: this(binders.ToArray())
private readonly IModelBindersProvider _modelBindersProvider;
private IReadOnlyList<IModelBinder> _binders;
/// <summary>
/// Initializes a new instance of the CompositeModelBinder class.
/// </summary>
/// <param name="modelBindersProvider">Provides a collection of <see cref="IModelBinder"/> instances.</param>
public CompositeModelBinder(IModelBindersProvider modelBindersProvider)
{
_modelBindersProvider = modelBindersProvider;
}
public CompositeModelBinder(params IModelBinder[] binders)
/// <inheritdoc />
public IReadOnlyList<IModelBinder> ModelBinders
{
Binders = binders;
get
{
if (_binders == null)
{
_binders = _modelBindersProvider.ModelBinders;
}
return _binders;
}
}
private IModelBinder[] Binders { get; set; }
public virtual async Task<bool> 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))
{

View File

@ -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
{
/// <summary>
/// Represents an aggregate of <see cref="IModelBinder"/> that delegates to one of the instances for model binding.
/// </summary>
public interface ICompositeModelBinder : IModelBinder
{
}
}

View File

@ -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
{
/// <summary>
/// Provides an activated collection of <see cref="IModelBinder"/> instances.
/// </summary>
public interface IModelBindersProvider
{
/// <summary>
/// Gets a collection of activated ModelBinders instances.
/// </summary>
IReadOnlyList<IModelBinder> ModelBinders { get; }
}
}

View File

@ -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
{
/// <summary>
/// Encapsulates information that describes an <see cref="IModelBinder"/>.
/// </summary>
public class ModelBinderDescriptor
{
/// <summary>
/// Creates a new instance of <see cref="ModelBinderDescriptor"/>.
/// </summary>
/// <param name="modelBinderType">A <see cref="IModelBinder/> type that the descriptor represents.</param>
public ModelBinderDescriptor([NotNull] Type modelBinderType)
{
var binderType = typeof(IModelBinder);
if (!binderType.IsAssignableFrom(modelBinderType))
{
var message = Resources.FormatTypeMustDeriveFromType(modelBinderType, binderType.FullName);
throw new ArgumentException(message, "modelBinderType");
}
ModelBinderType = modelBinderType;
}
/// <summary>
/// Creates a new instance of <see cref="ModelBinderDescriptor"/>.
/// </summary>
/// <param name="modelBinder">An instance of <see cref="IModelBinder"/> that the descriptor represents.</param>
public ModelBinderDescriptor([NotNull] IModelBinder modelBinder)
{
ModelBinder = modelBinder;
ModelBinderType = modelBinder.GetType();
}
/// <summary>
/// Gets the type of the <see cref="IModelBinder"/>.
/// </summary>
public Type ModelBinderType
{
get;
private set;
}
/// <summary>
/// Gets the instance of the <see cref="IModelBinder"/>.
/// </summary>
public IModelBinder ModelBinder
{
get;
private set;
}
}
}

View File

@ -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
{
/// <summary>
/// Extension methods for adding model binders to a collection.
/// </summary>
public static class ModelBinderDescriptorExtensions
{
/// <summary>
/// Adds a type representing a <see cref="IModelBinder"/> to a descriptor collection.
/// </summary>
/// <param name="descriptors">A list of ModelBinderDescriptors</param>
/// <param name="modelBinderType">Type representing an <see cref="IModelBinder"/>.</param>
/// <returns>ModelBinderDescriptor representing the added instance.</returns>
public static ModelBinderDescriptor Add([NotNull] this IList<ModelBinderDescriptor> descriptors,
[NotNull] Type modelBinderType)
{
var descriptor = new ModelBinderDescriptor(modelBinderType);
descriptors.Add(descriptor);
return descriptor;
}
/// <summary>
/// Inserts a type representing a <see cref="IModelBinder"/> to a descriptor collection.
/// </summary>
/// <param name="descriptors">A list of ModelBinderDescriptors</param>
/// <param name="modelBinderType">Type representing an <see cref="IModelBinder"/>.</param>
/// <returns>ModelBinderDescriptor representing the inserted instance.</returns>
public static ModelBinderDescriptor Insert([NotNull] this IList<ModelBinderDescriptor> 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;
}
/// <summary>
/// Adds an <see cref="IModelBinder"/> to a descriptor collection.
/// </summary>
/// <param name="descriptors">A list of ModelBinderDescriptors</param>
/// <param name="modelBinder">An <see cref="IModelBinder"/> instance.</param>
/// <returns>ModelBinderDescriptor representing the added instance.</returns>
public static ModelBinderDescriptor Add([NotNull] this IList<ModelBinderDescriptor> descriptors,
[NotNull] IModelBinder modelBinder)
{
var descriptor = new ModelBinderDescriptor(modelBinder);
descriptors.Add(descriptor);
return descriptor;
}
/// <summary>
/// Insert an <see cref="IModelBinder"/> to a descriptor collection.
/// </summary>
/// <param name="descriptors">A list of ModelBinderDescriptors</param>
/// <param name="modelBinder">An <see cref="IModelBinder"/> instance.</param>
/// <returns>ModelBinderDescriptor representing the added instance.</returns>
public static ModelBinderDescriptor Insert([NotNull] this IList<ModelBinderDescriptor> 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;
}
}
}

View File

@ -33,8 +33,12 @@
<Compile Include="Binders\CompositeModelBinder.cs" />
<Compile Include="Binders\DictionaryModelBinder.cs" />
<Compile Include="Binders\GenericModelBinder.cs" />
<Compile Include="Binders\ICompositeModelBinder.cs" />
<Compile Include="Binders\IModelBinder.cs" />
<Compile Include="Binders\IModelBindersProvider.cs" />
<Compile Include="Binders\KeyValuePairModelBinder.cs" />
<Compile Include="Binders\ModelBinderDescriptor.cs" />
<Compile Include="Binders\ModelBinderDescriptorExtensions.cs" />
<Compile Include="Binders\MutableObjectModelBinder.cs" />
<Compile Include="Binders\TypeConverterModelBinder.cs" />
<Compile Include="Binders\TypeMatchModelBinder.cs" />

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc
ReflectedActionDescriptorProvider>();
yield return describe.Transient<INestedProvider<ActionInvokerProviderContext>,
ReflectedActionInvokerProvider>();
yield return describe.Singleton<IActionDescriptorsCollectionProvider,
yield return describe.Singleton<IActionDescriptorsCollectionProvider,
DefaultActionDescriptorsCollectionProvider>();
yield return describe.Transient<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
@ -54,15 +54,12 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IValueProviderFactory, QueryStringValueProviderFactory>();
yield return describe.Transient<IValueProviderFactory, FormValueProviderFactory>();
yield return describe.Transient<IModelBinder, TypeConverterModelBinder>();
yield return describe.Transient<IModelBinder, TypeMatchModelBinder>();
yield return describe.Transient<IModelBinder, GenericModelBinder>();
yield return describe.Transient<IModelBinder, MutableObjectModelBinder>();
yield return describe.Transient<IModelBinder, ComplexModelDtoModelBinder>();
yield return describe.Transient<IInputFormatter, JsonInputFormatter>();
yield return describe.Transient<IInputFormatterProvider, TempInputFormatterProvider>();
yield return describe.Transient<IModelBindersProvider, DefaultModelBindersProvider>();
yield return describe.Transient<ICompositeModelBinder, CompositeModelBinder>();
yield return describe.Transient<INestedProvider<FilterProviderContext>, DefaultFilterProvider>();
yield return describe.Transient<IModelValidatorProvider, DataAnnotationsModelValidatorProvider>();
@ -72,15 +69,15 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IViewComponentSelector, DefaultViewComponentSelector>();
yield return describe.Transient<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
yield return describe.Transient<INestedProvider<ViewComponentInvokerProviderContext>,
yield return describe.Transient<INestedProvider<ViewComponentInvokerProviderContext>,
DefaultViewComponentInvokerProvider>();
yield return describe.Transient<IViewComponentHelper, DefaultViewComponentHelper>();
yield return describe.Transient<IAuthorizationService, DefaultAuthorizationService>();
yield return describe.Singleton<IClaimUidExtractor, DefaultClaimUidExtractor>();
yield return describe.Singleton<AntiForgery, AntiForgery>();
yield return describe.Singleton<IAntiForgeryAdditionalDataProvider,
DefaultAntiForgeryAdditionalDataProvider>();
yield return describe.Singleton<IAntiForgeryAdditionalDataProvider,
DefaultAntiForgeryAdditionalDataProvider>();
yield return
describe.Describe(

View File

@ -55,6 +55,7 @@
<Compile Include="DefaultControllerFactoryTest.cs" />
<Compile Include="Filters\ResultFilterAttributeTest.cs" />
<Compile Include="JsonResultTest.cs" />
<Compile Include="ParameterBinding\DefaultModelBindersProviderTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PropertyHelperTest.cs" />
<Compile Include="ReflectedActionDescriptorProviderTests.cs" />

View File

@ -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<IOptionsAccessor<MvcOptions>>();
optionsAccessor.SetupGet(o => o.Options)
.Returns(options);
var activator = new Mock<ITypeActivator>();
var serviceProvider = Mock.Of<IServiceProvider>();
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<GenericModelBinder>(binders[1]);
}
}
}
#endif

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
mockIntBinder
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
.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<IModelBinder>();
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<IModelBindersProvider>();
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<IModelBindersProvider>();
binderProvider.SetupGet(p => p.ModelBinders)
.Returns(new[] { mockIntBinder });
var shimBinder = new CompositeModelBinder(binderProvider.Object);
return shimBinder;
}
private class SimplePropertiesModel
{
public string FirstName { get; set; }

View File

@ -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<IModelBindersProvider>();
binderProviders.SetupGet(b => b.ModelBinders)
.Returns(new[] { CreateStringBinder(), CreateIntBinder() });
var innerBinder = new CompositeModelBinder(binderProviders.Object);
var valueProvider = new SimpleHttpValueProvider();
var bindingContext = GetBindingContext(valueProvider, innerBinder);

View File

@ -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<ModelBinderDescriptor>
{
new ModelBinderDescriptor(Mock.Of<IModelBinder>()),
new ModelBinderDescriptor(Mock.Of<IModelBinder>())
};
// Act & Assert
Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.Insert(index, typeof(IModelBinder)));
}
[Theory]
[InlineData(-2)]
[InlineData(3)]
public void Insert_WithInstance_ThrowsIfIndexIsOutOfBounds(int index)
{
// Arrange
var collection = new List<ModelBinderDescriptor>
{
new ModelBinderDescriptor(Mock.Of<IModelBinder>()),
new ModelBinderDescriptor(Mock.Of<IModelBinder>())
};
var binder = Mock.Of<IModelBinder>();
// Act & Assert
Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.Insert(index, binder));
}
[InlineData]
public void ModelBinderDescriptors_AddsTypesAndInstances()
{
// Arrange
var binder1 = Mock.Of<IModelBinder>();
var binder2 = Mock.Of<IModelBinder>();
var type1 = typeof(TypeMatchModelBinder);
var type2 = typeof(TypeConverterModelBinder);
var collection = new List<ModelBinderDescriptor>();
// 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

View File

@ -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);
}
}
}

View File

@ -29,6 +29,8 @@
<Compile Include="Binders\CompositeModelBinderTest.cs" />
<Compile Include="Binders\DictionaryModelBinderTest.cs" />
<Compile Include="Binders\KeyValuePairModelBinderTest.cs" />
<Compile Include="Binders\ModelBinderDescriptorExtensionsTest.cs" />
<Compile Include="Binders\ModelBinderDescriptorTest.cs" />
<Compile Include="Binders\ModelBindingContextTest.cs" />
<Compile Include="Binders\MutableObjectModelBinderTest.cs" />
<Compile Include="Binders\TypeConverterModelBinderTest.cs" />