// Copyright (c) .NET Foundation. 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 Microsoft.AspNetCore.Mvc.ModelBinding.Validation; namespace Microsoft.AspNetCore.Mvc.ModelBinding { /// /// A context that contains operating information for model binding and validation. /// public class DefaultModelBindingContext : ModelBindingContext { private static readonly IValueProvider EmptyValueProvider = new CompositeValueProvider(); private IValueProvider _originalValueProvider; private ActionContext _actionContext; private ModelStateDictionary _modelState; private ValidationStateDictionary _validationState; private State _state; private readonly Stack _stack = new Stack(); /// public override ActionContext ActionContext { get { return _actionContext; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _actionContext = value; } } /// public override string FieldName { get { return _state.FieldName; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _state.FieldName = value; } } /// public override object Model { get { return _state.Model; } set { _state.Model = value; } } /// public override ModelMetadata ModelMetadata { get { return _state.ModelMetadata; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _state.ModelMetadata = value; } } /// public override string ModelName { get { return _state.ModelName; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _state.ModelName = value; } } /// public override ModelStateDictionary ModelState { get { return _modelState; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _modelState = value; } } /// public override string BinderModelName { get { return _state.BinderModelName; } set { _state.BinderModelName = value; } } /// public override BindingSource BindingSource { get { return _state.BindingSource; } set { _state.BindingSource = value; } } /// public override bool IsTopLevelObject { get { return _state.IsTopLevelObject; } set { _state.IsTopLevelObject = value; } } /// /// Gets or sets the original value provider to be used when value providers are not filtered. /// public IValueProvider OriginalValueProvider { get { return _originalValueProvider; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _originalValueProvider = value; } } /// public override IValueProvider ValueProvider { get { return _state.ValueProvider; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _state.ValueProvider = value; } } /// public override Func PropertyFilter { get { return _state.PropertyFilter; } set { _state.PropertyFilter = value; } } /// public override ValidationStateDictionary ValidationState { get { return _validationState; } set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _validationState = value; } } /// public override ModelBindingResult Result { get { return _state.Result; } set { _state.Result = value; } } /// /// Creates a new for top-level model binding operation. /// /// /// The associated with the binding operation. /// /// The to use for binding. /// associated with the model. /// associated with the model. /// The name of the property or parameter being bound. /// A new instance of . public static ModelBindingContext CreateBindingContext( ActionContext actionContext, IValueProvider valueProvider, ModelMetadata metadata, BindingInfo bindingInfo, string modelName) { if (actionContext == null) { throw new ArgumentNullException(nameof(actionContext)); } if (valueProvider == null) { throw new ArgumentNullException(nameof(valueProvider)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (modelName == null) { throw new ArgumentNullException(nameof(modelName)); } var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName; var propertyFilterProvider = bindingInfo?.PropertyFilterProvider ?? metadata.PropertyFilterProvider; var bindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource; return new DefaultModelBindingContext() { ActionContext = actionContext, BinderModelName = binderModelName, BindingSource = bindingSource, PropertyFilter = propertyFilterProvider?.PropertyFilter, // Because this is the top-level context, FieldName and ModelName should be the same. FieldName = binderModelName ?? modelName, ModelName = binderModelName ?? modelName, IsTopLevelObject = true, ModelMetadata = metadata, ModelState = actionContext.ModelState, OriginalValueProvider = valueProvider, ValueProvider = FilterValueProvider(valueProvider, bindingSource), ValidationState = new ValidationStateDictionary(), }; } /// public override NestedScope EnterNestedScope( ModelMetadata modelMetadata, string fieldName, string modelName, object model) { if (modelMetadata == null) { throw new ArgumentNullException(nameof(modelMetadata)); } if (fieldName == null) { throw new ArgumentNullException(nameof(fieldName)); } if (modelName == null) { throw new ArgumentNullException(nameof(modelName)); } var scope = EnterNestedScope(); // Only filter if the new BindingSource affects the value providers. Otherwise we want // to preserve the current state. if (modelMetadata.BindingSource != null && !modelMetadata.BindingSource.IsGreedy) { ValueProvider = FilterValueProvider(OriginalValueProvider, modelMetadata.BindingSource); } Model = model; ModelMetadata = modelMetadata; ModelName = modelName; FieldName = fieldName; BinderModelName = modelMetadata.BinderModelName; BindingSource = modelMetadata.BindingSource; PropertyFilter = modelMetadata.PropertyFilterProvider?.PropertyFilter; IsTopLevelObject = false; return scope; } /// public override NestedScope EnterNestedScope() { _stack.Push(_state); Result = default; return new NestedScope(this); } /// protected override void ExitNestedScope() { _state = _stack.Pop(); } private static IValueProvider FilterValueProvider(IValueProvider valueProvider, BindingSource bindingSource) { if (bindingSource == null || bindingSource.IsGreedy) { return valueProvider; } if (!(valueProvider is IBindingSourceValueProvider bindingSourceValueProvider)) { return valueProvider; } return bindingSourceValueProvider.Filter(bindingSource) ?? EmptyValueProvider; } private struct State { public string FieldName; public object Model; public ModelMetadata ModelMetadata; public string ModelName; public IValueProvider ValueProvider; public Func PropertyFilter; public string BinderModelName; public BindingSource BindingSource; public bool IsTopLevelObject; public ModelBindingResult Result; } } }