| File: ModelBinding\Binders\KeyValuePairModelBinder.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders; /// <summary> /// An <see cref="IModelBinder"/> for <see cref="KeyValuePair{TKey, TValue}"/>. /// </summary> /// <typeparam name="TKey">The key type.</typeparam> /// <typeparam name="TValue">The value type.</typeparam> public class KeyValuePairModelBinder<TKey, TValue> : IModelBinder { private readonly IModelBinder _keyBinder; private readonly IModelBinder _valueBinder; private readonly ILogger _logger; /// <summary> /// Creates a new <see cref="KeyValuePair{TKey, TValue}"/>. /// </summary> /// <param name="keyBinder">The <see cref="IModelBinder"/> for <typeparamref name="TKey"/>.</param> /// <param name="valueBinder">The <see cref="IModelBinder"/> for <typeparamref name="TValue"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> public KeyValuePairModelBinder(IModelBinder keyBinder, IModelBinder valueBinder, ILoggerFactory loggerFactory) { ArgumentNullException.ThrowIfNull(keyBinder); ArgumentNullException.ThrowIfNull(valueBinder); ArgumentNullException.ThrowIfNull(loggerFactory); _keyBinder = keyBinder; _valueBinder = valueBinder; _logger = loggerFactory.CreateLogger(typeof(KeyValuePairModelBinder<TKey, TValue>)); } /// <inheritdoc /> public async Task BindModelAsync(ModelBindingContext bindingContext) { ArgumentNullException.ThrowIfNull(bindingContext); _logger.AttemptingToBindModel(bindingContext); var keyModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "Key"); var keyResult = await KeyValuePairModelBinder<TKey, TValue>.TryBindStrongModel<TKey?>(bindingContext, _keyBinder, "Key", keyModelName); var valueModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "Value"); var valueResult = await KeyValuePairModelBinder<TKey, TValue>.TryBindStrongModel<TValue?>(bindingContext, _valueBinder, "Value", valueModelName); if (keyResult.IsModelSet && valueResult.IsModelSet) { var model = new KeyValuePair<TKey?, TValue?>( ModelBindingHelper.CastOrDefault<TKey?>(keyResult.Model), ModelBindingHelper.CastOrDefault<TValue?>(valueResult.Model)); bindingContext.Result = ModelBindingResult.Success(model); _logger.DoneAttemptingToBindModel(bindingContext); return; } if (!keyResult.IsModelSet && valueResult.IsModelSet) { bindingContext.ModelState.TryAddModelError( keyModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingKeyOrValueAccessor()); _logger.DoneAttemptingToBindModel(bindingContext); return; } if (keyResult.IsModelSet && !valueResult.IsModelSet) { bindingContext.ModelState.TryAddModelError( valueModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingKeyOrValueAccessor()); _logger.DoneAttemptingToBindModel(bindingContext); return; } // If we failed to find data for a top-level model, then generate a // default 'empty' model and return it. if (bindingContext.IsTopLevelObject) { var model = new KeyValuePair<TKey?, TValue?>(); bindingContext.Result = ModelBindingResult.Success(model); } _logger.DoneAttemptingToBindModel(bindingContext); } internal static async Task<ModelBindingResult> TryBindStrongModel<TModel>( ModelBindingContext bindingContext, IModelBinder binder, string propertyName, string propertyModelName) { var propertyModelMetadata = bindingContext.ModelMetadata.Properties[propertyName]!; using (bindingContext.EnterNestedScope( modelMetadata: propertyModelMetadata, fieldName: propertyName, modelName: propertyModelName, model: null)) { await binder.BindModelAsync(bindingContext); return bindingContext.Result; } } }