Adding support for ModelMetadataAttribute in core.
This commit is contained in:
parent
ba8cf3ca46
commit
e9bcc3f0e8
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a <see cref="Type"/> which implements <see cref="IModelBinder"/> or
|
||||||
|
/// <see cref="IModelBinderProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBinderTypeProviderMetadata : IBinderMetadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Type"/> which implements either <see cref="IModelBinder"/> or
|
||||||
|
/// <see cref="IModelBinderProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
Type BinderType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute that can specify a model name or type of <see cref="IModelBinder"/> or
|
||||||
|
/// <see cref="IModelBinderProvider"/> to use for binding.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(
|
||||||
|
|
||||||
|
// Support method parameters in actions.
|
||||||
|
AttributeTargets.Parameter |
|
||||||
|
|
||||||
|
// Support properties on model DTOs.
|
||||||
|
AttributeTargets.Property |
|
||||||
|
|
||||||
|
// Support model types.
|
||||||
|
AttributeTargets.Class |
|
||||||
|
AttributeTargets.Enum |
|
||||||
|
AttributeTargets.Struct,
|
||||||
|
|
||||||
|
AllowMultiple = false,
|
||||||
|
Inherited = true)]
|
||||||
|
public class ModelBinderAttribute : Attribute, IModelNameProvider, IBinderTypeProviderMetadata
|
||||||
|
{
|
||||||
|
private Type _binderType;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Type BinderType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _binderType;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
if (!typeof(IModelBinder).IsAssignableFrom(value) &&
|
||||||
|
!typeof(IModelBinderProvider).IsAssignableFrom(value))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatBinderType_MustBeIModelBinderOrIModelBinderProvider(
|
||||||
|
value.FullName,
|
||||||
|
typeof(IModelBinder).FullName,
|
||||||
|
typeof(IModelBinderProvider).FullName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_binderType = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IModelBinder"/> which can bind a model based on the value of
|
||||||
|
/// <see cref="ModelMetadata.BinderType"/>. The supplied <see cref="IModelBinder"/>
|
||||||
|
/// type or <see cref="IModelBinderProvider"/> type will be used to bind the model.
|
||||||
|
/// </summary>
|
||||||
|
public class BinderTypeBasedModelBinder : IModelBinder
|
||||||
|
{
|
||||||
|
private readonly ITypeActivator _typeActivator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="BinderTypeBasedModelBinder"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeActivator">The <see cref="ITypeActivator"/>.</param>
|
||||||
|
public BinderTypeBasedModelBinder([NotNull] ITypeActivator typeActivator)
|
||||||
|
{
|
||||||
|
_typeActivator = typeActivator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
|
{
|
||||||
|
if (bindingContext.ModelMetadata.BinderType == null)
|
||||||
|
{
|
||||||
|
// Return false so that we are able to continue with the default set of model binders,
|
||||||
|
// if there is no specific model binder provided.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
|
||||||
|
var instance = _typeActivator.CreateInstance(requestServices, bindingContext.ModelMetadata.BinderType);
|
||||||
|
|
||||||
|
var modelBinder = instance as IModelBinder;
|
||||||
|
if (modelBinder == null)
|
||||||
|
{
|
||||||
|
var modelBinderProvider = instance as IModelBinderProvider;
|
||||||
|
if (modelBinderProvider != null)
|
||||||
|
{
|
||||||
|
modelBinder = new CompositeModelBinder(modelBinderProvider);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
Resources.FormatBinderType_MustBeIModelBinderOrIModelBinderProvider(
|
||||||
|
bindingContext.ModelMetadata.BinderType.FullName,
|
||||||
|
typeof(IModelBinder).FullName,
|
||||||
|
typeof(IModelBinderProvider).FullName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await modelBinder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// return true here, because this binder will handle all cases where the model binder is
|
||||||
|
// specified by metadata.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
BinderMetadata = attributes.OfType<IBinderMetadata>().FirstOrDefault();
|
BinderMetadata = attributes.OfType<IBinderMetadata>().FirstOrDefault();
|
||||||
PropertyBindingInfo = attributes.OfType<IPropertyBindingInfo>();
|
PropertyBindingInfo = attributes.OfType<IPropertyBindingInfo>();
|
||||||
BinderModelNameProvider = attributes.OfType<IModelNameProvider>().FirstOrDefault();
|
BinderModelNameProvider = attributes.OfType<IModelNameProvider>().FirstOrDefault();
|
||||||
|
BinderTypeProviders = attributes.OfType<IBinderTypeProviderMetadata>();
|
||||||
|
|
||||||
// Special case the [DisplayFormat] attribute hanging off an applied [DataType] attribute. This property is
|
// Special case the [DisplayFormat] attribute hanging off an applied [DataType] attribute. This property is
|
||||||
// non-null for DataType.Currency, DataType.Date, DataType.Time, and potentially custom [DataType]
|
// non-null for DataType.Currency, DataType.Date, DataType.Time, and potentially custom [DataType]
|
||||||
|
|
@ -35,6 +36,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets (or sets in subclasses) <see cref="IEnumerable{IBinderTypeProviderMetadata}"/> found in collection passed
|
||||||
|
/// to the <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<IBinderTypeProviderMetadata> BinderTypeProviders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets (or sets in subclasses) <see cref="IBinderMetadata"/> found in collection passed to the
|
/// Gets (or sets in subclasses) <see cref="IBinderMetadata"/> found in collection passed to the
|
||||||
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
|
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
|
||||||
|
|
@ -74,7 +81,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public HiddenInputAttribute HiddenInput { get; protected set; }
|
public HiddenInputAttribute HiddenInput { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets (or sets in subclasses) <see cref="IEnumerable{IModelPropertyBindingInfo}"/> found in collection
|
/// Gets (or sets in subclasses) <see cref="IEnumerable{IPropertyBindingInfo}"/> found in collection
|
||||||
/// passed to the <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor,
|
/// passed to the <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor,
|
||||||
/// if any.
|
/// if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Type ComputeBinderType()
|
||||||
|
{
|
||||||
|
if (PrototypeCache.BinderTypeProviders != null)
|
||||||
|
{
|
||||||
|
// The need for fallback here is to handle cases where a model binder is specified
|
||||||
|
// on a type and on a parameter to an action.
|
||||||
|
//
|
||||||
|
// We want to respect the value set by the parameter (if any), and use the value specifed
|
||||||
|
// on the type as a fallback.
|
||||||
|
//
|
||||||
|
// We generalize this process, in case someone adds ordered providers (with count > 2) through
|
||||||
|
// extensibility.
|
||||||
|
foreach (var provider in PrototypeCache.BinderTypeProviders)
|
||||||
|
{
|
||||||
|
if (provider.BinderType != null)
|
||||||
|
{
|
||||||
|
return provider.BinderType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.ComputeBinderType();
|
||||||
|
}
|
||||||
|
|
||||||
protected override IBinderMetadata ComputeBinderMetadata()
|
protected override IBinderMetadata ComputeBinderMetadata()
|
||||||
{
|
{
|
||||||
return PrototypeCache.BinderMetadata != null
|
return PrototypeCache.BinderMetadata != null
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
private string _binderModelName;
|
private string _binderModelName;
|
||||||
private IReadOnlyList<string> _binderIncludeProperties;
|
private IReadOnlyList<string> _binderIncludeProperties;
|
||||||
private IReadOnlyList<string> _binderExcludeProperties;
|
private IReadOnlyList<string> _binderExcludeProperties;
|
||||||
|
private Type _binderType;
|
||||||
|
|
||||||
private bool _convertEmptyStringToNullComputed;
|
private bool _convertEmptyStringToNullComputed;
|
||||||
private bool _nullDisplayTextComputed;
|
private bool _nullDisplayTextComputed;
|
||||||
|
|
@ -55,6 +56,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
private bool _isBinderIncludePropertiesComputed;
|
private bool _isBinderIncludePropertiesComputed;
|
||||||
private bool _isBinderModelNameComputed;
|
private bool _isBinderModelNameComputed;
|
||||||
private bool _isBinderExcludePropertiesComputed;
|
private bool _isBinderExcludePropertiesComputed;
|
||||||
|
private bool _isBinderTypeComputed;
|
||||||
|
|
||||||
// Constructor for creating real instances of the metadata class based on a prototype
|
// Constructor for creating real instances of the metadata class based on a prototype
|
||||||
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
|
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
|
||||||
|
|
@ -473,8 +475,32 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public sealed override Type BinderType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_isBinderTypeComputed)
|
||||||
|
{
|
||||||
|
_binderType = ComputeBinderType();
|
||||||
|
_isBinderTypeComputed = true;
|
||||||
|
}
|
||||||
|
return _binderType;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_binderType = value;
|
||||||
|
_isBinderTypeComputed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected TPrototypeCache PrototypeCache { get; set; }
|
protected TPrototypeCache PrototypeCache { get; set; }
|
||||||
|
|
||||||
|
protected virtual Type ComputeBinderType()
|
||||||
|
{
|
||||||
|
return base.BinderType;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual IBinderMetadata ComputeBinderMetadata()
|
protected virtual IBinderMetadata ComputeBinderMetadata()
|
||||||
{
|
{
|
||||||
return base.BinderMetadata;
|
return base.BinderMetadata;
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the name of a model if specified explicitly using <see cref="IModelNameProvider"/>.
|
/// The name of the model if specified explicitly using <see cref="IModelNameProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string BinderModelName { get; set; }
|
public virtual string BinderModelName { get; set; }
|
||||||
|
|
||||||
|
|
@ -62,6 +62,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual IReadOnlyList<string> BinderExcludeProperties { get; set; }
|
public virtual IReadOnlyList<string> BinderExcludeProperties { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Type"/> of an <see cref="IModelBinder"/> or an <see cref="IModelBinderProvider"/>
|
||||||
|
/// of a model if specified explicitly using <see cref="IBinderTypeProviderMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
public virtual Type BinderType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a binder metadata for this model.
|
/// Gets or sets a binder metadata for this model.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return GetString("ModelStateDictionary_MaxModelStateErrors");
|
return GetString("ModelStateDictionary_MaxModelStateErrors");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type '{0}' must implement either '{1}' or '{2}' to be used as a model binder.
|
||||||
|
/// </summary>
|
||||||
|
internal static string BinderType_MustBeIModelBinderOrIModelBinderProvider
|
||||||
|
{
|
||||||
|
get { return GetString("BinderType_MustBeIModelBinderOrIModelBinderProvider"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type '{0}' must implement either '{1}' or '{2}' to be used as a model binder.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatBinderType_MustBeIModelBinderOrIModelBinderProvider(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("BinderType_MustBeIModelBinderOrIModelBinderProvider"), p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -198,4 +198,7 @@
|
||||||
<data name="ModelStateDictionary_MaxModelStateErrors" xml:space="preserve">
|
<data name="ModelStateDictionary_MaxModelStateErrors" xml:space="preserve">
|
||||||
<value>The maximum number of allowed model errors has been reached.</value>
|
<value>The maximum number of allowed model errors has been reached.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="BinderType_MustBeIModelBinderOrIModelBinderProvider" xml:space="preserve">
|
||||||
|
<value>The type '{0}' must implement either '{1}' or '{2}' to be used as a model binder.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
|
||||||
|
|
||||||
namespace System.Web.Http
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An attribute that specifies that the value can be bound by a model binder.
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
|
||||||
public class ModelBinderAttribute : Attribute, IBinderMetadata
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
options.ViewEngines.Add(typeof(RazorViewEngine));
|
options.ViewEngines.Add(typeof(RazorViewEngine));
|
||||||
|
|
||||||
// Set up ModelBinding
|
// Set up ModelBinding
|
||||||
|
options.ModelBinders.Add(typeof(BinderTypeBasedModelBinder));
|
||||||
options.ModelBinders.Add(typeof(ServicesModelBinder));
|
options.ModelBinders.Add(typeof(ServicesModelBinder));
|
||||||
options.ModelBinders.Add(typeof(BodyModelBinder));
|
options.ModelBinders.Add(typeof(BodyModelBinder));
|
||||||
options.ModelBinders.Add(new TypeConverterModelBinder());
|
options.ModelBinders.Add(new TypeConverterModelBinder());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using ModelBindingWebSite;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class ModelBindingModelBinderAttributeTest
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ModelBindingWebSite));
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ModelBinderAttribute_CustomModelPrefix()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// [ModelBinder(Name = "customPrefix")] is used to apply a prefix
|
||||||
|
var url =
|
||||||
|
"http://localhost/ModelBinderAttribute_Company/GetCompany?customPrefix.Employees[0].Name=somename";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||||
|
|
||||||
|
var employee = Assert.Single(company.Employees);
|
||||||
|
Assert.Equal("somename", employee.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GetBinderType_UseModelBinderOnType")]
|
||||||
|
[InlineData("GetBinderType_UseModelBinderProviderOnType")]
|
||||||
|
public async Task ModelBinderAttribute_WithPrefixOnParameter(string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// [ModelBinder(Name = "customPrefix")] is used to apply a prefix
|
||||||
|
var url =
|
||||||
|
"http://localhost/ModelBinderAttribute_Product/" +
|
||||||
|
action +
|
||||||
|
"?customPrefix.ProductId=5";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(
|
||||||
|
"ModelBindingWebSite.Controllers.ModelBinderAttribute_ProductController+ProductModelBinder",
|
||||||
|
body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GetBinderType_UseModelBinder")]
|
||||||
|
[InlineData("GetBinderType_UseModelBinderProvider")]
|
||||||
|
public async Task ModelBinderAttribute_WithBinderOnParameter(string action)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url =
|
||||||
|
"http://localhost/ModelBinderAttribute_Product/" +
|
||||||
|
action +
|
||||||
|
"?model.productId=5";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(
|
||||||
|
"ModelBindingWebSite.Controllers.ModelBinderAttribute_ProductController+ProductModelBinder",
|
||||||
|
body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ModelBinderAttribute_WithBinderOnEnum()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url =
|
||||||
|
"http://localhost/ModelBinderAttribute_Product/" +
|
||||||
|
"ModelBinderAttribute_UseModelBinderOnEnum" +
|
||||||
|
"?status=Shipped";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync(url);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("StatusShipped", body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Product
|
||||||
|
{
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
public string BinderType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -64,8 +64,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await client.PostAsync("http://localhost/Home/GetCustomer?Id=1234", content);
|
var response = await client.PostAsync("http://localhost/Home/GetCustomer?Id=1234", content);
|
||||||
|
|
||||||
//Assert
|
// Assert
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
var customer = JsonConvert.DeserializeObject<Customer>(
|
var customer = JsonConvert.DeserializeObject<Customer>(
|
||||||
await response.Content.ReadAsStringAsync());
|
await response.Content.ReadAsStringAsync());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
#if ASPNET50
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
{
|
||||||
|
public class BinderTypeBasedModelBinderModelBinderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_ReturnsFalseIfNoBinderTypeIsSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person));
|
||||||
|
|
||||||
|
var binder = new BinderTypeBasedModelBinder(Mock.Of<ITypeActivator>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(binderResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_ReturnsTrueEvenIfSelectedBinderReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person));
|
||||||
|
bindingContext.ModelMetadata.BinderType = typeof(FalseModelBinder);
|
||||||
|
|
||||||
|
var innerModelBinder = new FalseModelBinder();
|
||||||
|
|
||||||
|
var mockITypeActivator = new Mock<ITypeActivator>();
|
||||||
|
mockITypeActivator
|
||||||
|
.Setup(o => o.CreateInstance(It.IsAny<IServiceProvider>(), typeof(FalseModelBinder)))
|
||||||
|
.Returns(innerModelBinder);
|
||||||
|
|
||||||
|
var binder = new BinderTypeBasedModelBinder(mockITypeActivator.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(binderResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_CallsBindAsync_OnProvidedModelBinder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person));
|
||||||
|
bindingContext.ModelMetadata.BinderType = typeof(TrueModelBinder);
|
||||||
|
|
||||||
|
var model = new Person();
|
||||||
|
var innerModelBinder = new TrueModelBinder(model);
|
||||||
|
|
||||||
|
var mockITypeActivator = new Mock<ITypeActivator>();
|
||||||
|
mockITypeActivator
|
||||||
|
.Setup(o => o.CreateInstance(It.IsAny<IServiceProvider>(), typeof(TrueModelBinder)))
|
||||||
|
.Returns(innerModelBinder);
|
||||||
|
|
||||||
|
var binder = new BinderTypeBasedModelBinder(mockITypeActivator.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(binderResult);
|
||||||
|
Assert.Same(model, bindingContext.Model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_CallsBindAsync_OnProvidedModelBinderProvider()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person));
|
||||||
|
bindingContext.ModelMetadata.BinderType = typeof(ModelBinderProvider);
|
||||||
|
|
||||||
|
var model = new Person();
|
||||||
|
var innerModelBinder = new TrueModelBinder(model);
|
||||||
|
|
||||||
|
var provider = new ModelBinderProvider(innerModelBinder);
|
||||||
|
|
||||||
|
var mockITypeActivator = new Mock<ITypeActivator>();
|
||||||
|
mockITypeActivator
|
||||||
|
.Setup(o => o.CreateInstance(It.IsAny<IServiceProvider>(), typeof(ModelBinderProvider)))
|
||||||
|
.Returns(provider);
|
||||||
|
|
||||||
|
var binder = new BinderTypeBasedModelBinder(mockITypeActivator.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(binderResult);
|
||||||
|
Assert.Same(model, bindingContext.Model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_ForNonModelBinderAndModelBinderProviderTypes_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person));
|
||||||
|
bindingContext.ModelMetadata.BinderType = typeof(string);
|
||||||
|
var binder = new BinderTypeBasedModelBinder(Mock.Of<ITypeActivator>());
|
||||||
|
|
||||||
|
var expected = "The type 'System.String' must implement either " +
|
||||||
|
"'Microsoft.AspNet.Mvc.ModelBinding.IModelBinder' or " +
|
||||||
|
"'Microsoft.AspNet.Mvc.ModelBinding.IModelBinderProvider' to be used as a model binder.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => binder.BindModelAsync(bindingContext));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelBindingContext GetBindingContext(Type modelType)
|
||||||
|
{
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
var operationBindingContext = new OperationBindingContext
|
||||||
|
{
|
||||||
|
MetadataProvider = metadataProvider,
|
||||||
|
HttpContext = new DefaultHttpContext(),
|
||||||
|
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var bindingContext = new ModelBindingContext
|
||||||
|
{
|
||||||
|
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||||
|
ModelName = "someName",
|
||||||
|
ValueProvider = Mock.Of<IValueProvider>(),
|
||||||
|
ModelState = new ModelStateDictionary(),
|
||||||
|
OperationBindingContext = operationBindingContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
return bindingContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Person
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int Age { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FalseModelBinder : IModelBinder
|
||||||
|
{
|
||||||
|
public Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TrueModelBinder : IModelBinder
|
||||||
|
{
|
||||||
|
private readonly object _model;
|
||||||
|
|
||||||
|
public TrueModelBinder(object model)
|
||||||
|
{
|
||||||
|
_model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
|
{
|
||||||
|
bindingContext.Model = _model;
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModelBinderProvider : IModelBinderProvider
|
||||||
|
{
|
||||||
|
private readonly IModelBinder _inner;
|
||||||
|
|
||||||
|
public ModelBinderProvider(IModelBinder inner)
|
||||||
|
{
|
||||||
|
_inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IModelBinder> ModelBinders
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new List<IModelBinder>() { _inner, };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
public class ModelBinderAttributeTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void InvalidBinderType_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new ModelBinderAttribute();
|
||||||
|
|
||||||
|
var expected =
|
||||||
|
"The type 'System.String' must implement either " +
|
||||||
|
"'Microsoft.AspNet.Mvc.ModelBinding.IModelBinder' or " +
|
||||||
|
"'Microsoft.AspNet.Mvc.ModelBinding.IModelBinderProvider' to be used as a model binder.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => { attribute.BinderType = typeof(string); });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var i = 0;
|
var i = 0;
|
||||||
Assert.Equal(9, mvcOptions.ModelBinders.Count);
|
Assert.Equal(10, mvcOptions.ModelBinders.Count);
|
||||||
|
Assert.Equal(typeof(BinderTypeBasedModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
||||||
Assert.Equal(typeof(ServicesModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
Assert.Equal(typeof(ServicesModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
||||||
Assert.Equal(typeof(BodyModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
Assert.Equal(typeof(BodyModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
||||||
Assert.Equal(typeof(TypeConverterModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
Assert.Equal(typeof(TypeConverterModelBinder), mvcOptions.ModelBinders[i++].OptionType);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
[Route("ModelBinderAttribute_Company/[action]")]
|
||||||
|
public class ModelBinderAttribute_CompanyController : Controller
|
||||||
|
{
|
||||||
|
// Uses Name to set a custom prefix
|
||||||
|
public Company GetCompany([ModelBinder(Name = "customPrefix")] Company company)
|
||||||
|
{
|
||||||
|
return company;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
[Route("ModelBinderAttribute_Product/[action]")]
|
||||||
|
public class ModelBinderAttribute_ProductController : Controller
|
||||||
|
{
|
||||||
|
public string GetBinderType_UseModelBinderOnType(
|
||||||
|
[ModelBinder(Name = "customPrefix")] ProductWithBinderOnType model)
|
||||||
|
{
|
||||||
|
return model.BinderType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBinderType_UseModelBinderProviderOnType(
|
||||||
|
[ModelBinder(Name = "customPrefix")] ProductWithBinderProviderOnType model)
|
||||||
|
{
|
||||||
|
return model.BinderType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBinderType_UseModelBinder(
|
||||||
|
[ModelBinder(BinderType = typeof(ProductModelBinder))] Product model)
|
||||||
|
{
|
||||||
|
return model.BinderType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBinderType_UseModelBinderProvider(
|
||||||
|
[ModelBinder(BinderType = typeof(ProductModelBinderProvider))] Product model)
|
||||||
|
{
|
||||||
|
return model.BinderType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBinderType_UseModelBinderOnProperty(Order order)
|
||||||
|
{
|
||||||
|
return order.Product.BinderType.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ModelBinderAttribute_UseModelBinderOnEnum(OrderStatus status)
|
||||||
|
{
|
||||||
|
return status.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
// Will be set by the binder
|
||||||
|
public Type BinderType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[ModelBinder(BinderType = typeof(ProductModelBinder))]
|
||||||
|
public class ProductWithBinderOnType : Product
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ModelBinder(BinderType = typeof(ProductModelBinderProvider))]
|
||||||
|
public class ProductWithBinderProviderOnType : Product
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Order
|
||||||
|
{
|
||||||
|
[ModelBinder(BinderType = typeof(ProductModelBinder))]
|
||||||
|
public Product Product { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[ModelBinder(BinderType = typeof(OrderStatusBinder))]
|
||||||
|
public enum OrderStatus
|
||||||
|
{
|
||||||
|
StatusOutOfStock,
|
||||||
|
StatusShipped,
|
||||||
|
StatusRecieved,
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OrderStatusBinder : IModelBinder
|
||||||
|
{
|
||||||
|
public Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
|
{
|
||||||
|
if (typeof(OrderStatus).IsAssignableFrom(bindingContext.ModelType))
|
||||||
|
{
|
||||||
|
var request = bindingContext.OperationBindingContext.HttpContext.Request;
|
||||||
|
|
||||||
|
// Doing something slightly different here to make sure we don't get accidentally bound
|
||||||
|
// by the type converter binder.
|
||||||
|
OrderStatus model;
|
||||||
|
if (Enum.TryParse<OrderStatus>("Status" + request.Query.Get("status"), out model))
|
||||||
|
{
|
||||||
|
bindingContext.Model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProductModelBinder : IModelBinder
|
||||||
|
{
|
||||||
|
public async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
|
{
|
||||||
|
if (typeof(Product).IsAssignableFrom(bindingContext.ModelType))
|
||||||
|
{
|
||||||
|
var model = (Product)Activator.CreateInstance(bindingContext.ModelType);
|
||||||
|
|
||||||
|
model.BinderType = GetType();
|
||||||
|
|
||||||
|
var key =
|
||||||
|
string.IsNullOrEmpty(bindingContext.ModelName) ?
|
||||||
|
"productId" :
|
||||||
|
bindingContext.ModelName + "." + "productId";
|
||||||
|
|
||||||
|
var value = await bindingContext.ValueProvider.GetValueAsync(key);
|
||||||
|
model.ProductId = (int)value.ConvertTo(typeof(int));
|
||||||
|
|
||||||
|
bindingContext.Model = model;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProductModelBinderProvider : IModelBinderProvider
|
||||||
|
{
|
||||||
|
public IReadOnlyList<IModelBinder> ModelBinders
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[] { new ProductModelBinder() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue