/// <inheritdoc />
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            if (bindingContext.ModelType == typeof(CancellationToken))
            {
                // We need to force boxing now, so we can insert the same reference to the boxed CancellationToken
                // in both the ValidationState and ModelBindingResult.
                //
                // DO NOT simplify this code by removing the cast.
                var model = (object)bindingContext.OperationBindingContext.HttpContext.RequestAborted;
                bindingContext.ValidationState.Add(model, new ValidationStateEntry()
                {
                    SuppressValidation = true
                });
                bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
            }

            return(TaskCache.CompletedTask);
        }
예제 #2
0
        public async Task BindModelAsync_EnforcesTopLevelRequired()
        {
            // Arrange
            var actionContext     = GetControllerContext();
            var mockModelMetadata = CreateMockModelMetadata();

            mockModelMetadata.Setup(o => o.IsRequired).Returns(true);
            mockModelMetadata.Setup(o => o.DisplayName).Returns("My Display Name");
            mockModelMetadata.Setup(o => o.ValidatorMetadata).Returns(new[]
            {
                new RequiredAttribute()
            });

            var validator = new DataAnnotationsModelValidator(
                new ValidationAttributeAdapterProvider(),
                new RequiredAttribute(),
                stringLocalizer: null);

            var parameterBinder    = CreateParameterBinder(mockModelMetadata.Object, validator);
            var modelBindingResult = ModelBindingResult.Success(null);

            // Act
            var result = await parameterBinder.BindModelAsync(
                actionContext,
                CreateMockModelBinder(modelBindingResult),
                CreateMockValueProvider(),
                new ParameterDescriptor { Name = "myParam", ParameterType = typeof(Person) },
                mockModelMetadata.Object,
                "ignoredvalue");

            // Assert
            Assert.False(actionContext.ModelState.IsValid);
            Assert.Equal("myParam", actionContext.ModelState.Single().Key);
            Assert.Equal(
                new RequiredAttribute().FormatErrorMessage("My Display Name"),
                actionContext.ModelState.Single().Value.Errors.Single().ErrorMessage);
        }
예제 #3
0
        public async Task BindModelAsync_DoesNotEnforceTopLevelRequired_IfNotValidatingTopLevelNodes()
        {
            // Arrange
            var actionContext     = GetControllerContext();
            var mockModelMetadata = CreateMockModelMetadata();

            mockModelMetadata.Setup(o => o.IsRequired).Returns(true);
            mockModelMetadata.Setup(o => o.DisplayName).Returns("My Display Name");
            mockModelMetadata.Setup(o => o.ValidatorMetadata).Returns(new[]
            {
                new RequiredAttribute()
            });

            var validator = new DataAnnotationsModelValidator(
                new ValidationAttributeAdapterProvider(),
                new RequiredAttribute(),
                stringLocalizer: null);

            // Do not set AllowValidatingTopLevelNodes.
            var optionsAccessor    = Options.Create(new MvcOptions());
            var parameterBinder    = CreateParameterBinder(mockModelMetadata.Object, validator, optionsAccessor);
            var modelBindingResult = ModelBindingResult.Success(null);

            // Act
            var result = await parameterBinder.BindModelAsync(
                actionContext,
                CreateMockModelBinder(modelBindingResult),
                CreateMockValueProvider(),
                new ParameterDescriptor { Name = "myParam", ParameterType = typeof(Person) },
                mockModelMetadata.Object,
                "ignoredvalue");

            // Assert
            Assert.True(actionContext.ModelState.IsValid);
            Assert.Empty(actionContext.ModelState);
        }
예제 #4
0
        public async Task BindModelAsync_ForOverlappingParametersWithSuppressions_InValid_WithInValidSecondParameter()
        {
            // Arrange
            var parameterDescriptor = new ParameterDescriptor
            {
                Name          = "patchDocument",
                ParameterType = typeof(IJsonPatchDocument),
            };

            var actionContext = GetControllerContext();
            var modelState    = actionContext.ModelState;

            // First ModelState key is not empty to match SimpleTypeModelBinder.
            modelState.SetModelValue("id", "notAGuid", "notAGuid");
            modelState.AddModelError("id", "This is not valid.");

            // Second ModelState key is empty to match BodyModelBinder.
            modelState.AddModelError(string.Empty, "This is also not valid.");

            var modelMetadataProvider = new TestModelMetadataProvider();

            modelMetadataProvider.ForType <IJsonPatchDocument>().ValidationDetails(v => v.ValidateChildren = false);
            var modelMetadata = modelMetadataProvider.GetMetadataForType(typeof(IJsonPatchDocument));

            var parameterBinder = new ParameterBinder(
                modelMetadataProvider,
                Mock.Of <IModelBinderFactory>(),
                new DefaultObjectValidator(
                    modelMetadataProvider,
                    new[] { TestModelValidatorProvider.CreateDefaultProvider() },
                    new MvcOptions()),
                _optionsAccessor,
                NullLoggerFactory.Instance);

            var modelBindingResult = ModelBindingResult.Failed();
            var modelBinder        = CreateMockModelBinder(modelBindingResult);

            // Act
            var result = await parameterBinder.BindModelAsync(
                actionContext,
                modelBinder,
                new SimpleValueProvider(),
                parameterDescriptor,
                modelMetadata,
                value : null);

            // Assert
            Assert.False(result.IsModelSet);
            Assert.False(modelState.IsValid);
            Assert.Collection(
                modelState,
                kvp =>
            {
                Assert.Empty(kvp.Key);
                Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState);
                var error = Assert.Single(kvp.Value.Errors);
                Assert.Equal("This is also not valid.", error.ErrorMessage);
            },
                kvp =>
            {
                Assert.Equal("id", kvp.Key);
                Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState);
                var error = Assert.Single(kvp.Value.Errors);
                Assert.Equal("This is not valid.", error.ErrorMessage);
            });
        }
예제 #5
0
        /// <inheritdoc />
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            // This method is optimized to use cached tasks when possible and avoid allocating
            // using Task.FromResult or async state machines.

            var allowedBindingSource = bindingContext.BindingSource;

            if (allowedBindingSource == null ||
                !allowedBindingSource.CanAcceptDataFrom(BindingSource.Header))
            {
                // Headers are opt-in. This model either didn't specify [FromHeader] or specified something
                // incompatible so let other binders run.
                return(TaskCache.CompletedTask);
            }

            var request = bindingContext.OperationBindingContext.HttpContext.Request;

            // Property name can be null if the model metadata represents a type (rather than a property or parameter).
            var headerName = bindingContext.FieldName;

            object model;

            if (ModelBindingHelper.CanGetCompatibleCollection <string>(bindingContext))
            {
                if (bindingContext.ModelType == typeof(string))
                {
                    var value = request.Headers[headerName];
                    model = (string)value;
                }
                else
                {
                    var values = request.Headers.GetCommaSeparatedValues(headerName);
                    model = GetCompatibleCollection(bindingContext, values);
                }
            }
            else
            {
                // An unsupported datatype or a new collection is needed (perhaps because target type is an array) but
                // can't assign it to the property.
                model = null;
            }

            if (model == null)
            {
                // Silently fail if unable to create an instance or use the current instance. Also reach here in the
                // typeof(string) case if the header does not exist in the request and in the
                // typeof(IEnumerable<string>) case if the header does not exist and this is not a top-level object.
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
            }
            else
            {
                bindingContext.ModelState.SetModelValue(
                    bindingContext.ModelName,
                    request.Headers.GetCommaSeparatedValues(headerName),
                    request.Headers[headerName]);

                bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
            }

            return(TaskCache.CompletedTask);
        }
예제 #6
0
        /// <summary>
        /// Binds a model specified by <paramref name="parameter"/> using <paramref name="value"/> as the initial value.
        /// </summary>
        /// <param name="actionContext">The <see cref="ActionContext"/>.</param>
        /// <param name="modelBinder">The <see cref="IModelBinder"/>.</param>
        /// <param name="valueProvider">The <see cref="IValueProvider"/>.</param>
        /// <param name="parameter">The <see cref="ParameterDescriptor"/></param>
        /// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
        /// <param name="value">The initial model value.</param>
        /// <returns>The result of model binding.</returns>
        public virtual async Task <ModelBindingResult> BindModelAsync(
            ActionContext actionContext,
            IModelBinder modelBinder,
            IValueProvider valueProvider,
            ParameterDescriptor parameter,
            ModelMetadata metadata,
            object value)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException(nameof(actionContext));
            }

            if (modelBinder == null)
            {
                throw new ArgumentNullException(nameof(modelBinder));
            }

            if (valueProvider == null)
            {
                throw new ArgumentNullException(nameof(valueProvider));
            }

            if (parameter == null)
            {
                throw new ArgumentNullException(nameof(parameter));
            }

            if (metadata == null)
            {
                throw new ArgumentNullException(nameof(metadata));
            }

            if (parameter.BindingInfo?.RequestPredicate?.Invoke(actionContext) == false)
            {
                return(ModelBindingResult.Failed());
            }

            var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
                actionContext,
                valueProvider,
                metadata,
                parameter.BindingInfo,
                parameter.Name);

            modelBindingContext.Model = value;

            Logger.AttemptingToBindParameterOrProperty(parameter, modelBindingContext);

            var parameterModelName = parameter.BindingInfo?.BinderModelName ?? metadata.BinderModelName;

            if (parameterModelName != null)
            {
                // The name was set explicitly, always use that as the prefix.
                modelBindingContext.ModelName = parameterModelName;
            }
            else if (modelBindingContext.ValueProvider.ContainsPrefix(parameter.Name))
            {
                // We have a match for the parameter name, use that as that prefix.
                modelBindingContext.ModelName = parameter.Name;
            }
            else
            {
                // No match, fallback to empty string as the prefix.
                modelBindingContext.ModelName = string.Empty;
            }

            await modelBinder.BindModelAsync(modelBindingContext);

            Logger.DoneAttemptingToBindParameterOrProperty(parameter, modelBindingContext);

            var modelBindingResult = modelBindingContext.Result;

            if (_mvcOptions.AllowValidatingTopLevelNodes &&
                _objectModelValidator is ObjectModelValidator baseObjectValidator)
            {
                Logger.AttemptingToValidateParameterOrProperty(parameter, modelBindingContext);

                EnforceBindRequiredAndValidate(
                    baseObjectValidator,
                    actionContext,
                    parameter,
                    metadata,
                    modelBindingContext,
                    modelBindingResult);

                Logger.DoneAttemptingToValidateParameterOrProperty(parameter, modelBindingContext);
            }
            else
            {
                // For legacy implementations (which directly implemented IObjectModelValidator), fall back to the
                // back-compatibility logic. In this scenario, top-level validation attributes will be ignored like
                // they were historically.
                if (modelBindingResult.IsModelSet)
                {
                    _objectModelValidator.Validate(
                        actionContext,
                        modelBindingContext.ValidationState,
                        modelBindingContext.ModelName,
                        modelBindingResult.Model);
                }
            }

            return(modelBindingResult);
        }
예제 #7
0
        /// <summary>
        /// Attempts to bind the model using formatters.
        /// </summary>
        /// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param>
        /// <returns>
        /// A <see cref="Task{ModelBindingResult}"/> which when completed returns a <see cref="ModelBindingResult"/>.
        /// </returns>
        private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            // Special logic for body, treat the model name as string.Empty for the top level
            // object, but allow an override via BinderModelName. The purpose of this is to try
            // and be similar to the behavior for POCOs bound via traditional model binding.
            string modelBindingKey;

            if (bindingContext.IsTopLevelObject)
            {
                modelBindingKey = bindingContext.BinderModelName ?? string.Empty;
            }
            else
            {
                modelBindingKey = bindingContext.ModelName;
            }

            var httpContext = bindingContext.OperationBindingContext.HttpContext;

            var formatterContext = new InputFormatterContext(
                httpContext,
                modelBindingKey,
                bindingContext.ModelState,
                bindingContext.ModelMetadata,
                _readerFactory);

            var formatters = bindingContext.OperationBindingContext.InputFormatters;
            var formatter  = formatters.FirstOrDefault(f => f.CanRead(formatterContext));

            if (formatter == null)
            {
                var message = Resources.FormatUnsupportedContentType(
                    bindingContext.OperationBindingContext.HttpContext.Request.ContentType);

                var exception = new UnsupportedContentTypeException(message);
                bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);

                // This model binder is the only handler for the Body binding source and it cannot run twice. Always
                // tell the model binding system to skip other model binders and never to fall back i.e. indicate a
                // fatal error.
                bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
                return;
            }

            try
            {
                var previousCount = bindingContext.ModelState.ErrorCount;
                var result        = await formatter.ReadAsync(formatterContext);

                var model = result.Model;

                if (result.HasError)
                {
                    // Formatter encountered an error. Do not use the model it returned. As above, tell the model
                    // binding system to skip other model binders and never to fall back.
                    bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
                    return;
                }

                bindingContext.Result = ModelBindingResult.Success(modelBindingKey, model);
                return;
            }
            catch (Exception ex)
            {
                bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata);

                // This model binder is the only handler for the Body binding source and it cannot run twice. Always
                // tell the model binding system to skip other model binders and never to fall back i.e. indicate a
                // fatal error.
                bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
                return;
            }
        }
예제 #8
0
        private void EnforceBindRequiredAndValidate(ActionContext actionContext, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult)
        {
            if (!modelBindingResult.IsModelSet && metadata.IsBindingRequired)
            {
                // Enforce BindingBehavior.Required (e.g., [BindRequired])
                var modelName = modelBindingContext.FieldName;
                var message   = metadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(modelName);
                actionContext.ModelState.TryAddModelError(modelName, message);
            }
            else if (modelBindingResult.IsModelSet || metadata.IsRequired)
            {
                // Enforce any other validation rules
                var visitor = new ValidationVisitor(
                    actionContext,
                    _validatorProvider,
                    _validatorCache,
                    _modelMetadataProvider,
                    modelBindingContext.ValidationState);

                visitor.Validate(
                    metadata,
                    modelBindingContext.ModelName,
                    modelBindingResult.Model,
                    alwaysValidateAtTopLevel: metadata.IsRequired);
            }
        }
예제 #9
0
        /// <summary>
        /// Binds a model specified by <paramref name="parameter"/> using <paramref name="value"/> as the initial value.
        /// </summary>
        /// <param name="actionContext">The <see cref="ActionContext"/>.</param>
        /// <param name="modelBinder">The <see cref="IModelBinder"/>.</param>
        /// <param name="valueProvider">The <see cref="IValueProvider"/>.</param>
        /// <param name="parameter">The <see cref="ParameterDescriptor"/></param>
        /// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
        /// <param name="value">The initial model value.</param>
        /// <returns>The result of model binding.</returns>
        public virtual async Task <ModelBindingResult> BindModelAsync(
            ActionContext actionContext,
            IModelBinder modelBinder,
            IValueProvider valueProvider,
            ParameterDescriptor parameter,
            ModelMetadata metadata,
            object value)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException(nameof(actionContext));
            }

            if (modelBinder == null)
            {
                throw new ArgumentNullException(nameof(modelBinder));
            }

            if (valueProvider == null)
            {
                throw new ArgumentNullException(nameof(valueProvider));
            }

            if (parameter == null)
            {
                throw new ArgumentNullException(nameof(parameter));
            }

            if (metadata == null)
            {
                throw new ArgumentNullException(nameof(metadata));
            }

            if (parameter.BindingInfo?.RequestPredicate?.Invoke(actionContext) == false)
            {
                return(ModelBindingResult.Failed());
            }

            var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
                actionContext,
                valueProvider,
                metadata,
                parameter.BindingInfo,
                parameter.Name);

            modelBindingContext.Model = value;

            var parameterModelName = parameter.BindingInfo?.BinderModelName ?? metadata.BinderModelName;

            if (parameterModelName != null)
            {
                // The name was set explicitly, always use that as the prefix.
                modelBindingContext.ModelName = parameterModelName;
            }
            else if (modelBindingContext.ValueProvider.ContainsPrefix(parameter.Name))
            {
                // We have a match for the parameter name, use that as that prefix.
                modelBindingContext.ModelName = parameter.Name;
            }
            else
            {
                // No match, fallback to empty string as the prefix.
                modelBindingContext.ModelName = string.Empty;
            }

            await modelBinder.BindModelAsync(modelBindingContext);

            var modelBindingResult = modelBindingContext.Result;

            if (_validatorForBackCompatOnly != null)
            {
                // Since we don't have access to an IModelValidatorProvider, fall back
                // on back-compatibility logic. In this scenario, top-level validation
                // attributes will be ignored like they were historically.
                if (modelBindingResult.IsModelSet)
                {
                    _validatorForBackCompatOnly.Validate(
                        actionContext,
                        modelBindingContext.ValidationState,
                        modelBindingContext.ModelName,
                        modelBindingResult.Model);
                }
            }
            else
            {
                EnforceBindRequiredAndValidate(
                    actionContext,
                    metadata,
                    modelBindingContext,
                    modelBindingResult);
            }

            return(modelBindingResult);
        }
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            // This method is optimized to use cached tasks when possible and avoid allocating
            // using Task.FromResult. If you need to make changes of this nature, profile
            // allocations afterwards and look for Task<ModelBindingResult>.

            if (bindingContext.ModelMetadata.IsComplexType)
            {
                // this type cannot be converted
                return(TaskCache.CompletedTask);
            }

            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (valueProviderResult == ValueProviderResult.None)
            {
                // no entry
                return(TaskCache.CompletedTask);
            }

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            try
            {
                var model = valueProviderResult.ConvertTo(bindingContext.ModelType);

                if (bindingContext.ModelType == typeof(string))
                {
                    var modelAsString = model as string;
                    if (bindingContext.ModelMetadata.ConvertEmptyStringToNull &&
                        string.IsNullOrEmpty(modelAsString))
                    {
                        model = null;
                    }
                }

                // When converting newModel a null value may indicate a failed conversion for an otherwise required
                // model (can't set a ValueType to null). This detects if a null model value is acceptable given the
                // current bindingContext. If not, an error is logged.
                if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType)
                {
                    bindingContext.ModelState.TryAddModelError(
                        bindingContext.ModelName,
                        bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
                            valueProviderResult.ToString()));

                    bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                    return(TaskCache.CompletedTask);
                }
                else
                {
                    bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
                    return(TaskCache.CompletedTask);
                }
            }
            catch (Exception exception)
            {
                bindingContext.ModelState.TryAddModelError(
                    bindingContext.ModelName,
                    exception,
                    bindingContext.ModelMetadata);

                // Were able to find a converter for the type but conversion failed.
                // Tell the model binding system to skip other model binders.
                bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
                return(TaskCache.CompletedTask);
            }
        }
        /// <inheritdoc />
        public virtual async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            ModelBindingHelper.ValidateBindingContext(bindingContext);

            var model = bindingContext.Model;

            if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
            {
                // If we failed to find data for a top-level model, then generate a
                // default 'empty' model (or use existing Model) and return it.
                if (bindingContext.IsTopLevelObject)
                {
                    if (model == null)
                    {
                        model = CreateEmptyCollection(bindingContext.ModelType);
                    }

                    bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
                }

                return;
            }

            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            CollectionResult result;

            if (valueProviderResult == ValueProviderResult.None)
            {
                result = await BindComplexCollection(bindingContext);
            }
            else
            {
                result = await BindSimpleCollection(bindingContext, valueProviderResult);
            }

            var boundCollection = result.Model;

            if (model == null)
            {
                model = ConvertToCollectionType(bindingContext.ModelType, boundCollection);
            }
            else
            {
                // Special case for TryUpdateModelAsync(collection, ...) scenarios. Model is null in all other cases.
                CopyToModel(model, boundCollection);
            }

            Debug.Assert(model != null);
            if (result.ValidationStrategy != null)
            {
                bindingContext.ValidationState.Add(model, new ValidationStateEntry()
                {
                    Strategy = result.ValidationStrategy,
                });
            }

            if (valueProviderResult != ValueProviderResult.None)
            {
                // If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName.
                // If we did complex binding, there will already be an entry for each index.
                bindingContext.ModelState.SetModelValue(
                    bindingContext.ModelName,
                    valueProviderResult);
            }

            bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
        }
        /// <inheritdoc/>
        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            Check.NotNull(bindingContext, nameof(bindingContext));

            _logger.AttemptingToBindModel(bindingContext);

            var modelName           = bindingContext.ModelName;
            var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

            if (valueProviderResult == ValueProviderResult.None)
            {
                _logger.FoundNoValueInRequest(bindingContext);

                // no entry
                _logger.DoneAttemptingToBindModel(bindingContext);
                return;
            }

            var modelState = bindingContext.ModelState;

            modelState.SetModelValue(modelName, valueProviderResult);

            var metadata = bindingContext.ModelMetadata;
            var type     = metadata.UnderlyingOrModelType;

            try
            {
                var value = valueProviderResult.FirstValue;

                object?model;
                if (value is null)
                {
                    model = null;
                }
                else if (type == typeof(DateTimeOffset))
                {
                    if (DateTimeOffset.TryParse(value, valueProviderResult.Culture, DateTimeStyles.None | DateTimeStyles.AllowWhiteSpaces, out var actValue))
                    {
                        model = actValue;
                    }
                    else
                    {
                        model = null;
                    }

                    if (_dateTimeOffsetBinderOptions.HasValue && long.TryParse(value, out var timestamp))
                    {
                        switch (_dateTimeOffsetBinderOptions)
                        {
                        case DateTimeOffsetBinderOptions.SupportSeconds:
                            model = DateTimeOffset.FromUnixTimeSeconds(timestamp);
                            break;

                        case DateTimeOffsetBinderOptions.SupportMilliseconds:
                            model = DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
                            break;

                        default:
                            model = null;
                            break;
                        }
                    }
                }
                else
                {
                    throw new NotSupportedException();
                }

                if (model == null && !metadata.IsReferenceOrNullableType)
                {
                    modelState.TryAddModelError(
                        modelName,
                        metadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
                            valueProviderResult.ToString()));
                }
                else
                {
                    bindingContext.Result = ModelBindingResult.Success(model);
                }
            }
            catch (Exception exception)
            {
                modelState.TryAddModelError(modelName, exception, metadata);
            }

            _logger.DoneAttemptingToBindModel(bindingContext);
            await Task.CompletedTask;
        }