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

Commit fb81a5e

Browse files
committed
Introducing ModelBinderFactory
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.
1 parent 7d43851 commit fb81a5e

File tree

109 files changed

+3270
-2031
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+3270
-2031
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.AspNetCore.Mvc.ModelBinding
5+
{
6+
/// <summary>
7+
/// Creates <see cref="IModelBinder"/> instances. Register <see cref="IModelBinderProvider"/>
8+
/// instances in <c>MvcOptions</c>.
9+
/// </summary>
10+
public interface IModelBinderProvider
11+
{
12+
/// <summary>
13+
/// Creates a <see cref="IModelBinder"/> based on <see cref="ModelBinderProviderContext"/>.
14+
/// </summary>
15+
/// <param name="context">The <see cref="ModelBinderProviderContext"/>.</param>
16+
/// <returns>An <see cref="IModelBinder"/>.</returns>
17+
IModelBinder GetBinder(ModelBinderProviderContext context);
18+
}
19+
}

src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/Metadata/ModelMetadataIdentity.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33

44
using System;
55
using Microsoft.AspNetCore.Mvc.Abstractions;
6+
using Microsoft.Extensions.Internal;
67

78
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
89
{
910
/// <summary>
1011
/// A key type which identifies a <see cref="ModelMetadata"/>.
1112
/// </summary>
12-
public struct ModelMetadataIdentity
13+
public struct ModelMetadataIdentity : IEquatable<ModelMetadataIdentity>
1314
{
1415
/// <summary>
1516
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided model <see cref="Type"/>.
@@ -98,5 +99,31 @@ public ModelMetadataKind MetadataKind
9899
/// the current instance represents a type.
99100
/// </summary>
100101
public string Name { get; private set; }
102+
103+
/// <inheritdoc />
104+
public bool Equals(ModelMetadataIdentity other)
105+
{
106+
return
107+
ContainerType == other.ContainerType &&
108+
ModelType == other.ModelType &&
109+
Name == other.Name;
110+
}
111+
112+
/// <inheritdoc />
113+
public override bool Equals(object obj)
114+
{
115+
var other = obj as ModelMetadataIdentity?;
116+
return other.HasValue && Equals(other.Value);
117+
}
118+
119+
/// <inheritdoc />
120+
public override int GetHashCode()
121+
{
122+
var hash = new HashCodeCombiner();
123+
hash.Add(ContainerType);
124+
hash.Add(ModelType);
125+
hash.Add(Name, StringComparer.Ordinal);
126+
return hash;
127+
}
101128
}
102129
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.AspNetCore.Mvc.ModelBinding
5+
{
6+
/// <summary>
7+
/// A context object for <see cref="IModelBinderProvider.GetBinder"/>.
8+
/// </summary>
9+
public abstract class ModelBinderProviderContext
10+
{
11+
/// <summary>
12+
/// Creates an <see cref="IModelBinder"/> for the given <paramref name="metadata"/>.
13+
/// </summary>
14+
/// <param name="metadata">The <see cref="ModelMetadata"/> for the model.</param>
15+
/// <returns>An <see cref="IModelBinder"/>.</returns>
16+
public abstract IModelBinder CreateBinder(ModelMetadata metadata);
17+
18+
/// <summary>
19+
/// Gets the <see cref="BindingInfo"/>. May be <c>null</c>.
20+
/// </summary>
21+
public abstract BindingInfo BindingInfo { get; }
22+
23+
/// <summary>
24+
/// Gets the <see cref="ModelMetadata"/>.
25+
/// </summary>
26+
public abstract ModelMetadata Metadata { get; }
27+
28+
/// <summary>
29+
/// Gets the <see cref="IModelMetadataProvider"/>.
30+
/// </summary>
31+
public abstract IModelMetadataProvider MetadataProvider { get; }
32+
}
33+
}

src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingContext.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,12 @@ public abstract class ModelBindingContext
1616
/// </summary>
1717
public abstract string BinderModelName { get; set; }
1818

19-
/// <summary>
20-
/// Gets the <see cref="Type"/> of an <see cref="IModelBinder"/> associated with the
21-
/// <see cref="Model"/>.
22-
/// </summary>
23-
public abstract Type BinderType { get; set; }
24-
2519
/// <summary>
2620
/// Gets or sets a value which represents the <see cref="ModelBinding.BindingSource"/> associated with the
2721
/// <see cref="Model"/>.
2822
/// </summary>
2923
public abstract BindingSource BindingSource { get; set; }
3024

31-
/// <summary>
32-
/// Gets or sets a value that indicates whether the binder should use an empty prefix to look up
33-
/// values in <see cref="IValueProvider"/> when no values are found using the <see cref="ModelName"/> prefix.
34-
/// </summary>
35-
/// <remarks>
36-
/// Passed into the model binding system. Should not be <c>true</c> when <see cref="IsTopLevelObject"/> is
37-
/// <c>false</c>.
38-
/// </remarks>
39-
public abstract bool FallbackToEmptyPrefix { get; set; }
40-
4125
/// <summary>
4226
/// Gets or sets the name of the current field being bound.
4327
/// </summary>

src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelMetadata.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
1616
/// A metadata representation of a model type, property or parameter.
1717
/// </summary>
1818
[DebuggerDisplay("{DebuggerToString(),nq}")]
19-
public abstract class ModelMetadata
19+
public abstract class ModelMetadata : IEquatable<ModelMetadata>
2020
{
2121
/// <summary>
2222
/// The default value of <see cref="ModelMetadata.Order"/>.
@@ -378,6 +378,36 @@ public string GetDisplayName()
378378
return DisplayName ?? PropertyName ?? ModelType.Name;
379379
}
380380

381+
/// <inheritdoc />
382+
public bool Equals(ModelMetadata other)
383+
{
384+
if (object.ReferenceEquals(this, other))
385+
{
386+
return true;
387+
}
388+
389+
if (other == null)
390+
{
391+
return false;
392+
}
393+
else
394+
{
395+
return Identity.Equals(other.Identity);
396+
}
397+
}
398+
399+
/// <inheritdoc />
400+
public override bool Equals(object obj)
401+
{
402+
return base.Equals(obj as ModelMetadata);
403+
}
404+
405+
/// <inheritdoc />
406+
public override int GetHashCode()
407+
{
408+
return Identity.GetHashCode();
409+
}
410+
381411
private void InitializeTypeInformation()
382412
{
383413
Debug.Assert(ModelType != null);

src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/OperationBindingContext.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,6 @@ public class OperationBindingContext
3535
/// </summary>
3636
public IValueProvider ValueProvider { get; set; }
3737

38-
/// <summary>
39-
/// Gets or sets the <see cref="IModelBinder"/> associated with this context.
40-
/// </summary>
41-
public IModelBinder ModelBinder { get; set; }
42-
4338
/// <summary>
4439
/// Gets or sets the <see cref="IModelMetadataProvider"/> associated with this context.
4540
/// </summary>

src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public abstract class ControllerBase
2727
{
2828
private ControllerContext _controllerContext;
2929
private IModelMetadataProvider _metadataProvider;
30+
private IModelBinderFactory _modelBinderFactory;
3031
private IObjectModelValidator _objectValidator;
3132
private IUrlHelper _url;
3233

@@ -141,6 +142,31 @@ public IModelMetadataProvider MetadataProvider
141142
}
142143
}
143144

145+
/// <summary>
146+
/// Gets or sets the <see cref="IModelBinderFactory"/>.
147+
/// </summary>
148+
public IModelBinderFactory ModelBinderFactory
149+
{
150+
get
151+
{
152+
if (_modelBinderFactory == null)
153+
{
154+
_modelBinderFactory = HttpContext?.RequestServices?.GetRequiredService<IModelBinderFactory>();
155+
}
156+
157+
return _modelBinderFactory;
158+
}
159+
set
160+
{
161+
if (value == null)
162+
{
163+
throw new ArgumentNullException(nameof(value));
164+
}
165+
166+
_modelBinderFactory = value;
167+
}
168+
}
169+
144170
/// <summary>
145171
/// Gets or sets the <see cref="IUrlHelper"/>.
146172
/// </summary>
@@ -1139,7 +1165,7 @@ public virtual Task<bool> TryUpdateModelAsync<TModel>(
11391165
prefix,
11401166
ControllerContext,
11411167
MetadataProvider,
1142-
new CompositeModelBinder(ControllerContext.ModelBinders),
1168+
ModelBinderFactory,
11431169
valueProvider,
11441170
ControllerContext.InputFormatters,
11451171
ObjectValidator,
@@ -1179,7 +1205,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
11791205
prefix,
11801206
ControllerContext,
11811207
MetadataProvider,
1182-
new CompositeModelBinder(ControllerContext.ModelBinders),
1208+
ModelBinderFactory,
11831209
new CompositeValueProvider(ControllerContext.ValueProviders),
11841210
ControllerContext.InputFormatters,
11851211
ObjectValidator,
@@ -1219,7 +1245,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
12191245
prefix,
12201246
ControllerContext,
12211247
MetadataProvider,
1222-
new CompositeModelBinder(ControllerContext.ModelBinders),
1248+
ModelBinderFactory,
12231249
new CompositeValueProvider(ControllerContext.ValueProviders),
12241250
ControllerContext.InputFormatters,
12251251
ObjectValidator,
@@ -1267,7 +1293,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
12671293
prefix,
12681294
ControllerContext,
12691295
MetadataProvider,
1270-
new CompositeModelBinder(ControllerContext.ModelBinders),
1296+
ModelBinderFactory,
12711297
valueProvider,
12721298
ControllerContext.InputFormatters,
12731299
ObjectValidator,
@@ -1314,7 +1340,7 @@ public Task<bool> TryUpdateModelAsync<TModel>(
13141340
prefix,
13151341
ControllerContext,
13161342
MetadataProvider,
1317-
new CompositeModelBinder(ControllerContext.ModelBinders),
1343+
ModelBinderFactory,
13181344
valueProvider,
13191345
ControllerContext.InputFormatters,
13201346
ObjectValidator,
@@ -1353,7 +1379,7 @@ public virtual Task<bool> TryUpdateModelAsync(
13531379
prefix,
13541380
ControllerContext,
13551381
MetadataProvider,
1356-
new CompositeModelBinder(ControllerContext.ModelBinders),
1382+
ModelBinderFactory,
13571383
new CompositeValueProvider(ControllerContext.ValueProviders),
13581384
ControllerContext.InputFormatters,
13591385
ObjectValidator,
@@ -1405,7 +1431,7 @@ public Task<bool> TryUpdateModelAsync(
14051431
prefix,
14061432
ControllerContext,
14071433
MetadataProvider,
1408-
new CompositeModelBinder(ControllerContext.ModelBinders),
1434+
ModelBinderFactory,
14091435
valueProvider,
14101436
ControllerContext.InputFormatters,
14111437
ObjectValidator,

src/Microsoft.AspNetCore.Mvc.Core/ControllerContext.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace Microsoft.AspNetCore.Mvc
1717
public class ControllerContext : ActionContext
1818
{
1919
private FormatterCollection<IInputFormatter> _inputFormatters;
20-
private IList<IModelBinder> _modelBinders;
2120
private IList<IModelValidatorProvider> _validatorProviders;
2221
private IList<IValueProvider> _valueProviders;
2322

@@ -81,31 +80,6 @@ public virtual FormatterCollection<IInputFormatter> InputFormatters
8180
}
8281
}
8382

84-
/// <summary>
85-
/// Gets or sets the list of <see cref="IModelBinder"/> instances for the current request.
86-
/// </summary>
87-
public virtual IList<IModelBinder> ModelBinders
88-
{
89-
get
90-
{
91-
if (_modelBinders == null)
92-
{
93-
_modelBinders = new List<IModelBinder>();
94-
}
95-
96-
return _modelBinders;
97-
}
98-
set
99-
{
100-
if (value == null)
101-
{
102-
throw new ArgumentNullException(nameof(value));
103-
}
104-
105-
_modelBinders = value;
106-
}
107-
}
108-
10983
/// <summary>
11084
/// Gets or sets the list of <see cref="IModelValidatorProvider"/> instances for the current request.
11185
/// </summary>

src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/MvcCoreServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ internal static void AddMvcCoreServices(IServiceCollection services)
143143
var options = serviceProvider.GetRequiredService<IOptions<MvcOptions>>().Value;
144144
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
145145
}));
146+
services.TryAddSingleton<IModelBinderFactory, ModelBinderFactory>();
146147
services.TryAddSingleton<IObjectModelValidator, DefaultObjectValidator>();
147148
services.TryAddSingleton<ValidatorCache>();
148149
services.TryAddSingleton<ClientValidatorCache>();

src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvoker.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public ControllerActionInvoker(
3232
ControllerActionDescriptor descriptor,
3333
IReadOnlyList<IInputFormatter> inputFormatters,
3434
IControllerActionArgumentBinder argumentBinder,
35-
IReadOnlyList<IModelBinder> modelBinders,
3635
IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
3736
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
3837
ILogger logger,
@@ -42,7 +41,6 @@ public ControllerActionInvoker(
4241
actionContext,
4342
controllerActionInvokerCache,
4443
inputFormatters,
45-
modelBinders,
4644
modelValidatorProviders,
4745
valueProviderFactories,
4846
logger,

src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvokerProvider.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public class ControllerActionInvokerProvider : IActionInvokerProvider
2121
private readonly IControllerFactory _controllerFactory;
2222
private readonly ControllerActionInvokerCache _controllerActionInvokerCache;
2323
private readonly IReadOnlyList<IInputFormatter> _inputFormatters;
24-
private readonly IReadOnlyList<IModelBinder> _modelBinders;
2524
private readonly IReadOnlyList<IModelValidatorProvider> _modelValidatorProviders;
2625
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
2726
private readonly int _maxModelValidationErrors;
@@ -40,7 +39,6 @@ public ControllerActionInvokerProvider(
4039
_controllerActionInvokerCache = controllerActionInvokerCache;
4140
_argumentBinder = argumentBinder;
4241
_inputFormatters = optionsAccessor.Value.InputFormatters.ToArray();
43-
_modelBinders = optionsAccessor.Value.ModelBinders.ToArray();
4442
_modelValidatorProviders = optionsAccessor.Value.ModelValidatorProviders.ToArray();
4543
_valueProviderFactories = optionsAccessor.Value.ValueProviderFactories.ToArray();
4644
_maxModelValidationErrors = optionsAccessor.Value.MaxModelValidationErrors;
@@ -72,7 +70,6 @@ public void OnProvidersExecuting(ActionInvokerProviderContext context)
7270
actionDescriptor,
7371
_inputFormatters,
7472
_argumentBinder,
75-
_modelBinders,
7673
_modelValidatorProviders,
7774
_valueProviderFactories,
7875
_logger,

0 commit comments

Comments
 (0)