| File: Infrastructure\PageBinderFactory.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj (Microsoft.AspNetCore.Mvc.RazorPages) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Mvc.ModelBinding; namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; internal static class PageBinderFactory { internal static readonly Func<PageContext, object, Task> NullPropertyBinder = (context, arguments) => Task.CompletedTask; internal static readonly PageHandlerBinderDelegate NullHandlerBinder = (context, arguments) => Task.CompletedTask; public static Func<PageContext, object, Task> CreatePropertyBinder( ParameterBinder parameterBinder, IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, CompiledPageActionDescriptor actionDescriptor) { ArgumentNullException.ThrowIfNull(parameterBinder); ArgumentNullException.ThrowIfNull(actionDescriptor); var properties = actionDescriptor.BoundProperties; if (properties == null || properties.Count == 0) { return NullPropertyBinder; } var handlerType = actionDescriptor.HandlerTypeInfo.AsType(); var propertyBindingInfo = new BinderItem[properties.Count]; for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var metadata = modelMetadataProvider.GetMetadataForProperty(handlerType, property.Name); var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = property.BindingInfo, Metadata = metadata, CacheToken = property, }); propertyBindingInfo[i] = new BinderItem(binder, metadata); } return Bind; async Task Bind(PageContext pageContext, object instance) { var (success, valueProvider) = await CompositeValueProvider.TryCreateAsync(pageContext, pageContext.ValueProviderFactories); if (!success) { return; } for (var i = 0; i < properties.Count; i++) { var property = properties[i]; var bindingInfo = propertyBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( pageContext, bindingInfo.ModelBinder, valueProvider!, property, modelMetadata, value: null, container: instance); if (result.IsModelSet) { PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, instance, result.Model); } } } } public static PageHandlerBinderDelegate CreateHandlerBinder( ParameterBinder parameterBinder, IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory, CompiledPageActionDescriptor actionDescriptor, HandlerMethodDescriptor handler) { if (handler.Parameters == null || handler.Parameters.Count == 0) { return NullHandlerBinder; } var handlerType = actionDescriptor.HandlerTypeInfo.AsType(); var parameterBindingInfo = new BinderItem[handler.Parameters.Count]; for (var i = 0; i < parameterBindingInfo.Length; i++) { var parameter = handler.Parameters[i]; ModelMetadata metadata; if (modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase) { // The default model metadata provider derives from ModelMetadataProvider // and can therefore supply information about attributes applied to parameters. metadata = modelMetadataProviderBase.GetMetadataForParameter(parameter.ParameterInfo); } else { // For backward compatibility, if there's a custom model metadata provider that // only implements the older IModelMetadataProvider interface, access the more // limited metadata information it supplies. In this scenario, validation attributes // are not supported on parameters. metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType); } var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = parameter.BindingInfo, Metadata = metadata, CacheToken = parameter, }); parameterBindingInfo[i] = new BinderItem(binder, metadata); } return Bind; async Task Bind(PageContext pageContext, IDictionary<string, object?> arguments) { var (success, valueProvider) = await CompositeValueProvider.TryCreateAsync(pageContext, pageContext.ValueProviderFactories); if (!success) { return; } for (var i = 0; i < parameterBindingInfo.Length; i++) { var parameter = handler.Parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var result = await parameterBinder.BindModelAsync( pageContext, bindingInfo.ModelBinder, valueProvider!, parameter, modelMetadata, value: null, container: null); // Parameters do not have containers. if (result.IsModelSet) { arguments[parameter.Name] = result.Model; } } } } private readonly struct BinderItem { public BinderItem(IModelBinder modelBinder, ModelMetadata modelMetadata) { ModelMetadata = modelMetadata; ModelBinder = modelBinder; } public ModelMetadata ModelMetadata { get; } public IModelBinder ModelBinder { get; } } }