103 lines
4.6 KiB
C#
103 lines
4.6 KiB
C#
// 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.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNet.Mvc.Core;
|
|
|
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
|
{
|
|
public sealed class KeyValuePairModelBinder<TKey, TValue> : IModelBinder
|
|
{
|
|
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
|
{
|
|
ModelBindingHelper.ValidateBindingContext(bindingContext,
|
|
typeof(KeyValuePair<TKey, TValue>),
|
|
allowNullModel: true);
|
|
|
|
var childNodes = new List<ModelValidationNode>();
|
|
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key", childNodes);
|
|
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value", childNodes);
|
|
|
|
if (keyResult.IsModelSet && valueResult.IsModelSet)
|
|
{
|
|
var model = new KeyValuePair<TKey, TValue>(
|
|
ModelBindingHelper.CastOrDefault<TKey>(keyResult.Model),
|
|
ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model));
|
|
|
|
// Update the model for the top level validation node.
|
|
var modelValidationNode =
|
|
new ModelValidationNode(
|
|
bindingContext.ModelName,
|
|
bindingContext.ModelMetadata,
|
|
model,
|
|
childNodes);
|
|
|
|
// Success
|
|
return new ModelBindingResult(
|
|
model,
|
|
bindingContext.ModelName,
|
|
isModelSet: true,
|
|
validationNode: modelValidationNode);
|
|
}
|
|
else if (!keyResult.IsModelSet && valueResult.IsModelSet)
|
|
{
|
|
bindingContext.ModelState.TryAddModelError(
|
|
keyResult.Key,
|
|
Resources.KeyValuePair_BothKeyAndValueMustBePresent);
|
|
|
|
// Were able to get some data for this model.
|
|
// Always tell the model binding system to skip other model binders i.e. return non-null.
|
|
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);
|
|
}
|
|
else if (keyResult.IsModelSet && !valueResult.IsModelSet)
|
|
{
|
|
bindingContext.ModelState.TryAddModelError(
|
|
valueResult.Key,
|
|
Resources.KeyValuePair_BothKeyAndValueMustBePresent);
|
|
|
|
// Were able to get some data for this model.
|
|
// Always tell the model binding system to skip other model binders i.e. return non-null.
|
|
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);
|
|
}
|
|
else
|
|
{
|
|
// Caller (GenericModelBinder) was able to resolve a binder type and will create a ModelBindingResult
|
|
// that exits current ModelBinding loop.
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal async Task<ModelBindingResult> TryBindStrongModel<TModel>(
|
|
ModelBindingContext parentBindingContext,
|
|
string propertyName,
|
|
List<ModelValidationNode> childNodes)
|
|
{
|
|
var propertyModelMetadata =
|
|
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
|
|
var propertyModelName =
|
|
ModelNames.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
|
|
var propertyBindingContext = ModelBindingContext.GetChildModelBindingContext(
|
|
parentBindingContext,
|
|
propertyModelName,
|
|
propertyModelMetadata);
|
|
propertyBindingContext.BinderModelName = propertyModelMetadata.BinderModelName;
|
|
|
|
var modelBindingResult = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
|
|
propertyBindingContext);
|
|
if (modelBindingResult != null)
|
|
{
|
|
if (modelBindingResult.ValidationNode != null)
|
|
{
|
|
childNodes.Add(modelBindingResult.ValidationNode);
|
|
}
|
|
|
|
return modelBindingResult;
|
|
}
|
|
|
|
// Always return a ModelBindingResult to avoid an NRE in BindModelAsync.
|
|
return new ModelBindingResult(model: default(TModel), key: propertyModelName, isModelSet: false);
|
|
}
|
|
}
|
|
}
|