Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Introducing ModelBinderFactory
Browse files Browse the repository at this point in the history
This change separates model binding into IModelBinderProvider (decides
which binder to use) and IModelBinder (does binding). The
IModelBinderFactory is a new services with coordinates the creation of
model binders.
  • Loading branch information
rynowak committed Mar 29, 2016
1 parent 7d43851 commit fb81a5e
Show file tree
Hide file tree
Showing 109 changed files with 3,270 additions and 2,031 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
/// <summary>
/// Creates <see cref="IModelBinder"/> instances. Register <see cref="IModelBinderProvider"/>
/// instances in <c>MvcOptions</c>.
/// </summary>
public interface IModelBinderProvider
{
/// <summary>
/// Creates a <see cref="IModelBinder"/> based on <see cref="ModelBinderProviderContext"/>.
/// </summary>
/// <param name="context">The <see cref="ModelBinderProviderContext"/>.</param>
/// <returns>An <see cref="IModelBinder"/>.</returns>
IModelBinder GetBinder(ModelBinderProviderContext context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

using System;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A key type which identifies a <see cref="ModelMetadata"/>.
/// </summary>
public struct ModelMetadataIdentity
public struct ModelMetadataIdentity : IEquatable<ModelMetadataIdentity>
{
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided model <see cref="Type"/>.
Expand Down Expand Up @@ -98,5 +99,31 @@ public ModelMetadataKind MetadataKind
/// the current instance represents a type.
/// </summary>
public string Name { get; private set; }

/// <inheritdoc />
public bool Equals(ModelMetadataIdentity other)
{
return
ContainerType == other.ContainerType &&
ModelType == other.ModelType &&
Name == other.Name;
}

/// <inheritdoc />
public override bool Equals(object obj)
{
var other = obj as ModelMetadataIdentity?;
return other.HasValue && Equals(other.Value);
}

/// <inheritdoc />
public override int GetHashCode()
{
var hash = new HashCodeCombiner();
hash.Add(ContainerType);
hash.Add(ModelType);
hash.Add(Name, StringComparer.Ordinal);
return hash;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.

namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
/// <summary>
/// A context object for <see cref="IModelBinderProvider.GetBinder"/>.
/// </summary>
public abstract class ModelBinderProviderContext
{
/// <summary>
/// Creates an <see cref="IModelBinder"/> for the given <paramref name="metadata"/>.
/// </summary>
/// <param name="metadata">The <see cref="ModelMetadata"/> for the model.</param>
/// <returns>An <see cref="IModelBinder"/>.</returns>
public abstract IModelBinder CreateBinder(ModelMetadata metadata);

/// <summary>
/// Gets the <see cref="BindingInfo"/>. May be <c>null</c>.
/// </summary>
public abstract BindingInfo BindingInfo { get; }

/// <summary>
/// Gets the <see cref="ModelMetadata"/>.
/// </summary>
public abstract ModelMetadata Metadata { get; }

/// <summary>
/// Gets the <see cref="IModelMetadataProvider"/>.
/// </summary>
public abstract IModelMetadataProvider MetadataProvider { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,12 @@ public abstract class ModelBindingContext
/// </summary>
public abstract string BinderModelName { get; set; }

/// <summary>
/// Gets the <see cref="Type"/> of an <see cref="IModelBinder"/> associated with the
/// <see cref="Model"/>.
/// </summary>
public abstract Type BinderType { get; set; }

/// <summary>
/// Gets or sets a value which represents the <see cref="ModelBinding.BindingSource"/> associated with the
/// <see cref="Model"/>.
/// </summary>
public abstract BindingSource BindingSource { get; set; }

/// <summary>
/// Gets or sets a value that indicates whether the binder should use an empty prefix to look up
/// values in <see cref="IValueProvider"/> when no values are found using the <see cref="ModelName"/> prefix.
/// </summary>
/// <remarks>
/// Passed into the model binding system. Should not be <c>true</c> when <see cref="IsTopLevelObject"/> is
/// <c>false</c>.
/// </remarks>
public abstract bool FallbackToEmptyPrefix { get; set; }

/// <summary>
/// Gets or sets the name of the current field being bound.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// A metadata representation of a model type, property or parameter.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
public abstract class ModelMetadata
public abstract class ModelMetadata : IEquatable<ModelMetadata>
{
/// <summary>
/// The default value of <see cref="ModelMetadata.Order"/>.
Expand Down Expand Up @@ -378,6 +378,36 @@ public string GetDisplayName()
return DisplayName ?? PropertyName ?? ModelType.Name;
}

/// <inheritdoc />
public bool Equals(ModelMetadata other)
{
if (object.ReferenceEquals(this, other))
{
return true;
}

if (other == null)
{
return false;
}
else
{
return Identity.Equals(other.Identity);
}
}

/// <inheritdoc />
public override bool Equals(object obj)
{
return base.Equals(obj as ModelMetadata);
}

/// <inheritdoc />
public override int GetHashCode()
{
return Identity.GetHashCode();
}

private void InitializeTypeInformation()
{
Debug.Assert(ModelType != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ public class OperationBindingContext
/// </summary>
public IValueProvider ValueProvider { get; set; }

/// <summary>
/// Gets or sets the <see cref="IModelBinder"/> associated with this context.
/// </summary>
public IModelBinder ModelBinder { get; set; }

/// <summary>
/// Gets or sets the <see cref="IModelMetadataProvider"/> associated with this context.
/// </summary>
Expand Down
40 changes: 33 additions & 7 deletions src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public abstract class ControllerBase
{
private ControllerContext _controllerContext;
private IModelMetadataProvider _metadataProvider;
private IModelBinderFactory _modelBinderFactory;
private IObjectModelValidator _objectValidator;
private IUrlHelper _url;

Expand Down Expand Up @@ -141,6 +142,31 @@ public IModelMetadataProvider MetadataProvider
}
}

/// <summary>
/// Gets or sets the <see cref="IModelBinderFactory"/>.
/// </summary>
public IModelBinderFactory ModelBinderFactory
{
get
{
if (_modelBinderFactory == null)
{
_modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService<IModelBinderFactory>();
}

return _modelBinderFactory;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}

_modelBinderFactory = value;
}
}

/// <summary>
/// Gets or sets the <see cref="IUrlHelper"/>.
/// </summary>
Expand Down Expand Up @@ -1139,7 +1165,7 @@ public virtual Task<bool> TryUpdateModelAsync<TModel>(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
valueProvider,
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1179,7 +1205,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
new CompositeValueProvider(ControllerContext.ValueProviders),
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1219,7 +1245,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
new CompositeValueProvider(ControllerContext.ValueProviders),
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1267,7 +1293,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
valueProvider,
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1314,7 +1340,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
valueProvider,
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1353,7 +1379,7 @@ public virtual Task<bool> TryUpdateModelAsync(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
new CompositeValueProvider(ControllerContext.ValueProviders),
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down Expand Up @@ -1405,7 +1431,7 @@ public Task<bool> TryUpdateModelAsync(
prefix,
ControllerContext,
MetadataProvider,
new CompositeModelBinder(ControllerContext.ModelBinders),
ModelBinderFactory,
valueProvider,
ControllerContext.InputFormatters,
ObjectValidator,
Expand Down
26 changes: 0 additions & 26 deletions src/Microsoft.AspNetCore.Mvc.Core/ControllerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace Microsoft.AspNetCore.Mvc
public class ControllerContext : ActionContext
{
private FormatterCollection<IInputFormatter> _inputFormatters;
private IList<IModelBinder> _modelBinders;
private IList<IModelValidatorProvider> _validatorProviders;
private IList<IValueProvider> _valueProviders;

Expand Down Expand Up @@ -81,31 +80,6 @@ public virtual FormatterCollection<IInputFormatter> InputFormatters
}
}

/// <summary>
/// Gets or sets the list of <see cref="IModelBinder"/> instances for the current request.
/// </summary>
public virtual IList<IModelBinder> ModelBinders
{
get
{
if (_modelBinders == null)
{
_modelBinders = new List<IModelBinder>();
}

return _modelBinders;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}

_modelBinders = value;
}
}

/// <summary>
/// Gets or sets the list of <see cref="IModelValidatorProvider"/> instances for the current request.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ internal static void AddMvcCoreServices(IServiceCollection services)
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
}));
services.TryAddSingleton<IModelBinderFactory, ModelBinderFactory>();
services.TryAddSingleton<IObjectModelValidator, DefaultObjectValidator>();
services.TryAddSingleton<ValidatorCache>();
services.TryAddSingleton<ClientValidatorCache>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public ControllerActionInvoker(
ControllerActionDescriptor descriptor,
IReadOnlyList<IInputFormatter> inputFormatters,
IControllerActionArgumentBinder argumentBinder,
IReadOnlyList<IModelBinder> modelBinders,
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
ILogger logger,
Expand All @@ -42,7 +41,6 @@ public ControllerActionInvoker(
actionContext,
controllerActionInvokerCache,
inputFormatters,
modelBinders,
modelValidatorProviders,
valueProviderFactories,
logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class ControllerActionInvokerProvider : IActionInvokerProvider
private readonly IControllerFactory _controllerFactory;
private readonly ControllerActionInvokerCache _controllerActionInvokerCache;
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
private readonly IReadOnlyList<IModelBinder> _modelBinders;
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
private readonly int _maxModelValidationErrors;
Expand All @@ -40,7 +39,6 @@ public ControllerActionInvokerProvider(
_controllerActionInvokerCache = controllerActionInvokerCache;
_argumentBinder = argumentBinder;
_inputFormatters = optionsAccessor.Value.InputFormatters.ToArray();
_modelBinders = optionsAccessor.Value.ModelBinders.ToArray();
_modelValidatorProviders = optionsAccessor.Value.ModelValidatorProviders.ToArray();
_valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray();
_maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors;
Expand Down Expand Up @@ -72,7 +70,6 @@ public void OnProvidersExecuting(ActionInvokerProviderContext context)
actionDescriptor,
_inputFormatters,
_argumentBinder,
_modelBinders,
_modelValidatorProviders,
_valueProviderFactories,
_logger,
Expand Down
Loading

0 comments on commit fb81a5e

Please sign in to comment.