/// <inheritdoc /> public IModelBinder CreateBinder(ModelBinderFactoryContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } IModelBinder binder; if (TryGetCachedBinder(context.Metadata, context.CacheToken, out binder)) { return(binder); } // Perf: We're calling the Uncached version of the API here so we can: // 1. avoid allocating a context when the value is already cached // 2. avoid checking the cache twice when the value is not cached var providerContext = new DefaultModelBinderProviderContext(this, context); binder = CreateBinderCoreUncached(providerContext, context.CacheToken); if (binder == null) { var message = Resources.FormatCouldNotCreateIModelBinder(providerContext.Metadata.ModelType); throw new InvalidOperationException(message); } Debug.Assert(!(binder is PlaceholderBinder)); AddToCache(context.Metadata, context.CacheToken, binder); return(binder); }
public void CreateBinder_NestedProperties() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { Assert.NotNull(c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)])); return(Mock.Of <IModelBinder>()); } else if (c.Metadata.ModelType == typeof(WidgetId)) { return(Mock.Of <IModelBinder>()); } return(null); })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public void CreateBinder_Caches_NonRootNodes_WhenNonRootNodeReturnsNull() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); IModelBinder inner = null; var widgetProvider = new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { var binder = c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)]); Assert.IsType <NoOpBinder>(binder); if (inner == null) { inner = binder; } else { Assert.Same(inner, binder); } return(Mock.Of <IModelBinder>()); } return(null); }); var widgetIdProvider = new TestModelBinderProvider(c => { Assert.Equal(typeof(WidgetId), c.Metadata.ModelType); return(null); }); options.Value.ModelBinderProviders.Add(widgetProvider); options.Value.ModelBinderProviders.Add(widgetIdProvider); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), CacheToken = null, // We want the outermost provider to run twice. }; // Act var result1 = factory.CreateBinder(context); var result2 = factory.CreateBinder(context); // Assert Assert.NotSame(result1, result2); Assert.Equal(2, widgetProvider.SuccessCount); Assert.Equal(0, widgetIdProvider.SuccessCount); }
public void CreateBinder_Caches_WhenTokenIsNotNull() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager <MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { Assert.Equal(typeof(Employee), c.Metadata.ModelType); return(Mock.Of <IModelBinder>()); })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), CacheToken = new object(), }; // Act var result1 = factory.CreateBinder(context); var result2 = factory.CreateBinder(context); // Assert Assert.Same(result1, result2); }
/// <inheritdoc /> public IModelBinder CreateBinder(ModelBinderFactoryContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } // We perform caching in CreateBinder (not in CreateBinderCore) because we only want to // cache the top-level binder. IModelBinder binder; if (context.CacheToken != null && _cache.TryGetValue(context.CacheToken, out binder)) { return(binder); } var providerContext = new DefaultModelBinderProviderContext(this, context); binder = CreateBinderCore(providerContext, context.CacheToken); if (binder == null) { var message = Resources.FormatCouldNotCreateIModelBinder(providerContext.Metadata.ModelType); throw new InvalidOperationException(message); } if (context.CacheToken != null) { _cache.TryAdd(context.CacheToken, binder); } return(binder); }
public void CreateBinder_CreatesNoOpBinder_WhenPropertyDoesntHaveABinder() { // Arrange var metadataProvider = new TestModelMetadataProvider(); // There isn't a provider that can handle WidgetId. var options = new TestOptionsManager <MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { Assert.NotNull(c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)])); return(Mock.Of <IModelBinder>()); } return(null); })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public void CreateBinder_DoesNotCache_WhenTokenIsNull() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { Assert.Equal(typeof(Employee), c.Metadata.ModelType); return(Mock.Of <IModelBinder>()); })); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act var result1 = factory.CreateBinder(context); var result2 = factory.CreateBinder(context); // Assert Assert.NotSame(result1, result2); }
public void CreateBinder_Caches_NonRootNodes_FixesUpPlaceholderBinder() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager <MvcOptions>(); IModelBinder inner = null; IModelBinder innerInner = null; var widgetProvider = new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { inner = c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)]); return(Mock.Of <IModelBinder>()); } return(null); }); var widgetIdProvider = new TestModelBinderProvider(c => { Assert.Equal(typeof(WidgetId), c.Metadata.ModelType); innerInner = c.CreateBinder(c.Metadata); return(null); }); options.Value.ModelBinderProviders.Add(widgetProvider); options.Value.ModelBinderProviders.Add(widgetIdProvider); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), CacheToken = null, }; // Act 1 var result1 = factory.CreateBinder(context); context.Metadata = context.Metadata.Properties[nameof(Widget.Id)]; context.CacheToken = context.Metadata; // Act 2 var result2 = factory.CreateBinder(context); // Assert Assert.Same(inner, result2); Assert.NotSame(inner, innerInner); var placeholder = Assert.IsType <PlaceholderBinder>(innerInner); Assert.IsType <NoOpBinder>(placeholder.Inner); Assert.Equal(1, widgetProvider.SuccessCount); Assert.Equal(0, widgetIdProvider.SuccessCount); }
public void CreateBinder_Caches_NonRootNodes_UsesModelMetadataAsToken() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); IModelBinder inner = null; var widgetProvider = new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { inner = c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)]); return(Mock.Of <IModelBinder>()); } return(null); }); var widgetIdProvider = new TestModelBinderProvider(c => { Assert.Equal(typeof(WidgetId), c.Metadata.ModelType); return(Mock.Of <IModelBinder>()); }); options.Value.ModelBinderProviders.Add(widgetProvider); options.Value.ModelBinderProviders.Add(widgetIdProvider); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), CacheToken = null, }; // Act 1 var result1 = factory.CreateBinder(context); context.Metadata = context.Metadata.Properties[nameof(Widget.Id)]; context.CacheToken = context.Metadata; // Act 2 var result2 = factory.CreateBinder(context); // Assert Assert.Same(inner, result2); Assert.Equal(1, widgetProvider.SuccessCount); Assert.Equal(1, widgetIdProvider.SuccessCount); }
public DefaultModelBinderProviderContext( ModelBinderFactory factory, ModelBinderFactoryContext factoryContext) { _factory = factory; Metadata = factoryContext.Metadata; BindingInfo = factoryContext.BindingInfo; MetadataProvider = _factory._metadataProvider; Stack = new List <KeyValuePair <Key, PlaceholderBinder> >(); }
public void CreateBinder_PassesExpectedBindingInfo( BindingInfo parameterBindingInfo, BindingMetadata bindingMetadata, BindingInfo expectedInfo) { // Arrange var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType <Employee>().BindingDetails(binding => { binding.BinderModelName = bindingMetadata.BinderModelName; binding.BinderType = bindingMetadata.BinderType; binding.BindingSource = bindingMetadata.BindingSource; if (bindingMetadata.PropertyFilterProvider != null) { binding.PropertyFilterProvider = bindingMetadata.PropertyFilterProvider; } }); var modelBinder = Mock.Of <IModelBinder>(); var modelBinderProvider = new TestModelBinderProvider(context => { Assert.Equal(typeof(Employee), context.Metadata.ModelType); Assert.NotNull(context.BindingInfo); Assert.Equal(expectedInfo.BinderModelName, context.BindingInfo.BinderModelName, StringComparer.Ordinal); Assert.Equal(expectedInfo.BinderType, context.BindingInfo.BinderType); Assert.Equal(expectedInfo.BindingSource, context.BindingInfo.BindingSource); Assert.Same(expectedInfo.PropertyFilterProvider, context.BindingInfo.PropertyFilterProvider); return(modelBinder); }); var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Insert(0, modelBinderProvider); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var factoryContext = new ModelBinderFactoryContext { BindingInfo = parameterBindingInfo, Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act & Assert var result = factory.CreateBinder(factoryContext); // Confirm our IModelBinderProvider was called. Assert.Same(modelBinder, result); }
public void CreateBinder_Throws_WhenNoProviders() { // Arrange var expected = $"'{typeof(MvcOptions).FullName}.{nameof(MvcOptions.ModelBinderProviders)}' must not be " + $"empty. At least one '{typeof(IModelBinderProvider).FullName}' is required to model bind."; var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(string)), }; // Act & Assert var exception = Assert.Throws <InvalidOperationException>(() => factory.CreateBinder(context)); Assert.Equal(expected, exception.Message); }
public DefaultModelBinderProviderContext( ModelBinderFactory factory, ModelBinderFactoryContext factoryContext) { _factory = factory; Metadata = factoryContext.Metadata; BindingInfo = new BindingInfo { BinderModelName = factoryContext.BindingInfo?.BinderModelName ?? Metadata.BinderModelName, BinderType = factoryContext.BindingInfo?.BinderType ?? Metadata.BinderType, BindingSource = factoryContext.BindingInfo?.BindingSource ?? Metadata.BindingSource, PropertyFilterProvider = factoryContext.BindingInfo?.PropertyFilterProvider ?? Metadata.PropertyFilterProvider, }; MetadataProvider = _factory._metadataProvider; Visited = new Dictionary <Key, IModelBinder>(); }
public void CreateBinder_Throws_WhenBinderNotCreated() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager <MvcOptions>(); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(string)), }; // Act var exception = Assert.Throws <InvalidOperationException>(() => factory.CreateBinder(context)); // Assert Assert.Equal( $"Could not create a model binder for model object of type '{typeof(string).FullName}'.", exception.Message); }
public void CreateBinder_CreatesNoOpBinder_WhenPropertyBindingIsNotAllowed() { // Arrange var metadataProvider = new TestModelMetadataProvider(); metadataProvider .ForProperty <Widget>(nameof(Widget.Id)) .BindingDetails(m => m.IsBindingAllowed = false); var modelBinder = new ByteArrayModelBinder(NullLoggerFactory.Instance); var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(WidgetId)) { return(modelBinder); } return(null); })); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForProperty(typeof(Widget), nameof(Widget.Id)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); Assert.IsType <NoOpBinder>(result); }
public void CreateBinder_BreaksCycles() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var callCount = 0; var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { var currentCallCount = ++callCount; Assert.Equal(typeof(Employee), c.Metadata.ModelType); var binder = c.CreateBinder(c.Metadata.Properties[nameof(Employee.Manager)]); if (currentCallCount == 2) { Assert.IsType <PlaceholderBinder>(binder); } return(Mock.Of <IModelBinder>()); })); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public DefaultModelBinderProviderContext( ModelBinderFactory factory, ModelBinderFactoryContext factoryContext) { _factory = factory; Metadata = factoryContext.Metadata; BindingInfo bindingInfo; if (factoryContext.BindingInfo != null) { bindingInfo = new BindingInfo(factoryContext.BindingInfo); } else { bindingInfo = new BindingInfo(); } bindingInfo.TryApplyBindingInfo(Metadata); BindingInfo = bindingInfo; MetadataProvider = _factory._metadataProvider; Visited = new Dictionary<Key, IModelBinder?>(); }
public void CreateBinder_Throws_WhenBinderNotCreated() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = Options.Create(new MvcOptions()); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(_ => null)); var factory = new ModelBinderFactory( metadataProvider, options, GetServices()); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(string)), }; // Act & Assert var exception = Assert.Throws <InvalidOperationException>(() => factory.CreateBinder(context)); Assert.Equal( $"Could not create a model binder for model object of type '{typeof(string).FullName}'.", exception.Message); }
public void CreateBinder_Caches_NonRootNodes_UsesModelMetadataAsToken() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager<MvcOptions>(); IModelBinder inner = null; var widgetProvider = new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { inner = c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)]); return Mock.Of<IModelBinder>(); } return null; }); var widgetIdProvider = new TestModelBinderProvider(c => { Assert.Equal(typeof(WidgetId), c.Metadata.ModelType); return Mock.Of<IModelBinder>(); }); options.Value.ModelBinderProviders.Add(widgetProvider); options.Value.ModelBinderProviders.Add(widgetIdProvider); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), CacheToken = null, }; // Act 1 var result1 = factory.CreateBinder(context); context.Metadata = context.Metadata.Properties[nameof(Widget.Id)]; context.CacheToken = context.Metadata; // Act 2 var result2 = factory.CreateBinder(context); // Assert Assert.Same(inner, result2); Assert.Equal(1, widgetProvider.SuccessCount); Assert.Equal(1, widgetIdProvider.SuccessCount); }
public void CreateBinder_Caches_NonRootNodes_WhenNonRootNodeReturnsNull() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager<MvcOptions>(); IModelBinder inner = null; var widgetProvider = new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { var binder = c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)]); Assert.IsType<NoOpBinder>(binder); if (inner == null) { inner = binder; } else { Assert.Same(inner, binder); } return Mock.Of<IModelBinder>(); } return null; }); var widgetIdProvider = new TestModelBinderProvider(c => { Assert.Equal(typeof(WidgetId), c.Metadata.ModelType); return null; }); options.Value.ModelBinderProviders.Add(widgetProvider); options.Value.ModelBinderProviders.Add(widgetIdProvider); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), CacheToken = null, // We want the outermost provider to run twice. }; // Act var result1 = factory.CreateBinder(context); var result2 = factory.CreateBinder(context); // Assert Assert.NotSame(result1, result2); Assert.Equal(2, widgetProvider.SuccessCount); Assert.Equal(0, widgetIdProvider.SuccessCount); }
public void CreateBinder_CreatesNoOpBinder_WhenPropertyBindingIsNotAllowed() { // Arrange var metadataProvider = new TestModelMetadataProvider(); metadataProvider .ForProperty<Widget>(nameof(Widget.Id)) .BindingDetails(m => m.IsBindingAllowed = false); var modelBinder = new ByteArrayModelBinder(); var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(WidgetId)) { return modelBinder; } return null; })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForProperty(typeof(Widget), nameof(Widget.Id)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); Assert.IsType<NoOpBinder>(result); }
public void CreateBinder_CreatesNoOpBinder_WhenPropertyDoesntHaveABinder() { // Arrange var metadataProvider = new TestModelMetadataProvider(); // There isn't a provider that can handle WidgetId. var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { Assert.NotNull(c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)])); return Mock.Of<IModelBinder>(); } return null; })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public void CreateBinder_DoesNotCache_WhenTokenIsNull() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { Assert.Equal(typeof(Employee), c.Metadata.ModelType); return Mock.Of<IModelBinder>(); })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act var result1 = factory.CreateBinder(context); var result2 = factory.CreateBinder(context); // Assert Assert.NotSame(result1, result2); }
public void CreateBinder_NestedProperties() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { if (c.Metadata.ModelType == typeof(Widget)) { Assert.NotNull(c.CreateBinder(c.Metadata.Properties[nameof(Widget.Id)])); return Mock.Of<IModelBinder>(); } else if (c.Metadata.ModelType == typeof(WidgetId)) { return Mock.Of<IModelBinder>(); } return null; })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Widget)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public void CreateBinder_PassesExpectedBindingInfo( BindingInfo parameterBindingInfo, BindingMetadata bindingMetadata, BindingInfo expectedInfo) { // Arrange var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType<Employee>().BindingDetails(binding => { binding.BinderModelName = bindingMetadata.BinderModelName; binding.BinderType = bindingMetadata.BinderType; binding.BindingSource = bindingMetadata.BindingSource; if (bindingMetadata.PropertyFilterProvider != null) { binding.PropertyFilterProvider = bindingMetadata.PropertyFilterProvider; } }); var modelBinder = Mock.Of<IModelBinder>(); var modelBinderProvider = new TestModelBinderProvider(context => { Assert.Equal(typeof(Employee), context.Metadata.ModelType); Assert.NotNull(context.BindingInfo); Assert.Equal(expectedInfo.BinderModelName, context.BindingInfo.BinderModelName, StringComparer.Ordinal); Assert.Equal(expectedInfo.BinderType, context.BindingInfo.BinderType); Assert.Equal(expectedInfo.BindingSource, context.BindingInfo.BindingSource); Assert.Same(expectedInfo.PropertyFilterProvider, context.BindingInfo.PropertyFilterProvider); return modelBinder; }); var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Insert(0, modelBinderProvider); var factory = new ModelBinderFactory(metadataProvider, options); var factoryContext = new ModelBinderFactoryContext { BindingInfo = parameterBindingInfo, Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act & Assert var result = factory.CreateBinder(factoryContext); // Confirm our IModelBinderProvider was called. Assert.Same(modelBinder, result); }
public void CreateBinder_Throws_WhenBinderNotCreated() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var options = new TestOptionsManager<MvcOptions>(); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(string)), }; // Act var exception = Assert.Throws<InvalidOperationException>(() => factory.CreateBinder(context)); // Assert Assert.Equal( $"Could not create a model binder for model object of type '{typeof(string).FullName}'.", exception.Message); }
public void CreateBinder_BreaksCycles() { // Arrange var metadataProvider = new TestModelMetadataProvider(); var callCount = 0; var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Add(new TestModelBinderProvider(c => { var currentCallCount = ++callCount; Assert.Equal(typeof(Employee), c.Metadata.ModelType); var binder = c.CreateBinder(c.Metadata.Properties[nameof(Employee.Manager)]); if (currentCallCount == 2) { Assert.IsType<PlaceholderBinder>(binder); } return Mock.Of<IModelBinder>(); })); var factory = new ModelBinderFactory(metadataProvider, options); var context = new ModelBinderFactoryContext() { Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act var result = factory.CreateBinder(context); // Assert Assert.NotNull(result); }
public static async Task <bool> TryUpdateModelAsync( object model, Type modelType, string prefix, ActionContext actionContext, IModelMetadataProvider metadataProvider, IModelBinderFactory modelBinderFactory, IValueProvider valueProvider, IObjectModelValidator objectModelValidator, Func <ModelMetadata, bool> propertyFilter) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (modelType == null) { throw new ArgumentNullException(nameof(modelType)); } if (prefix == null) { throw new ArgumentNullException(nameof(prefix)); } if (actionContext == null) { throw new ArgumentNullException(nameof(actionContext)); } if (metadataProvider == null) { throw new ArgumentNullException(nameof(metadataProvider)); } if (modelBinderFactory == null) { throw new ArgumentNullException(nameof(modelBinderFactory)); } if (valueProvider == null) { throw new ArgumentNullException(nameof(valueProvider)); } if (objectModelValidator == null) { throw new ArgumentNullException(nameof(objectModelValidator)); } if (propertyFilter == null) { throw new ArgumentNullException(nameof(propertyFilter)); } if (!modelType.IsAssignableFrom(model.GetType())) { var message = Resources.FormatModelType_WrongType( model.GetType().FullName, modelType.FullName); throw new ArgumentException(message, nameof(modelType)); } var modelMetadata = metadataProvider.GetMetadataForType(modelType); if (modelMetadata.BoundConstructor != null) { throw new NotSupportedException(Resources.FormatTryUpdateModel_RecordTypeNotSupported(nameof(TryUpdateModelAsync), modelType)); } var modelState = actionContext.ModelState; var modelBindingContext = DefaultModelBindingContext.CreateBindingContext( actionContext, valueProvider, modelMetadata, bindingInfo: null, modelName: prefix); modelBindingContext.Model = model; modelBindingContext.PropertyFilter = propertyFilter; var factoryContext = new ModelBinderFactoryContext() { Metadata = modelMetadata, BindingInfo = new BindingInfo() { BinderModelName = modelMetadata.BinderModelName, BinderType = modelMetadata.BinderType, BindingSource = modelMetadata.BindingSource, PropertyFilterProvider = modelMetadata.PropertyFilterProvider, }, // We're using the model metadata as the cache token here so that TryUpdateModelAsync calls // for the same model type can share a binder. This won't overlap with normal model binding // operations because they use the ParameterDescriptor for the token. CacheToken = modelMetadata, }; var binder = modelBinderFactory.CreateBinder(factoryContext); await binder.BindModelAsync(modelBindingContext); var modelBindingResult = modelBindingContext.Result; if (modelBindingResult.IsModelSet) { objectModelValidator.Validate( actionContext, modelBindingContext.ValidationState, modelBindingContext.ModelName, modelBindingResult.Model); return(modelState.IsValid); } return(false); }