This repository has been archived by the owner on Dec 14, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
Copy pathModelMetadata.cs
478 lines (410 loc) · 19.4 KB
/
ModelMetadata.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
/// <summary>
/// A metadata representation of a model type, property or parameter.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
public abstract class ModelMetadata : IEquatable<ModelMetadata>
{
/// <summary>
/// The default value of <see cref="ModelMetadata.Order"/>.
/// </summary>
public static readonly int DefaultOrder = 10000;
private int? _hashCode;
/// <summary>
/// Creates a new <see cref="ModelMetadata"/>.
/// </summary>
/// <param name="identity">The <see cref="ModelMetadataIdentity"/>.</param>
protected ModelMetadata(ModelMetadataIdentity identity)
{
Identity = identity;
InitializeTypeInformation();
}
/// <summary>
/// Gets the container type of this metadata if it represents a property, otherwise <c>null</c>.
/// </summary>
public Type ContainerType { get { return Identity.ContainerType; } }
/// <summary>
/// Gets a value indicating the kind of metadata element represented by the current instance.
/// </summary>
public ModelMetadataKind MetadataKind { get { return Identity.MetadataKind; } }
/// <summary>
/// Gets the model type represented by the current instance.
/// </summary>
public Type ModelType { get { return Identity.ModelType; } }
/// <summary>
/// Gets the property name represented by the current instance.
/// </summary>
public string PropertyName
{
get
{
return Identity.Name;
}
}
/// <summary>
/// Gets the key for the current instance.
/// </summary>
protected ModelMetadataIdentity Identity { get; }
/// <summary>
/// Gets a collection of additional information about the model.
/// </summary>
public abstract IReadOnlyDictionary<object, object> AdditionalValues { get; }
/// <summary>
/// Gets the collection of <see cref="ModelMetadata"/> instances for the model's properties.
/// </summary>
public abstract ModelPropertyCollection Properties { get; }
/// <summary>
/// Gets the name of a model if specified explicitly using <see cref="IModelNameProvider"/>.
/// </summary>
public abstract string BinderModelName { get; }
/// <summary>
/// Gets the <see cref="Type"/> of an <see cref="IModelBinder"/> of a model if specified explicitly using
/// <see cref="IBinderTypeProviderMetadata"/>.
/// </summary>
public abstract Type BinderType { get; }
/// <summary>
/// Gets a binder metadata for this model.
/// </summary>
public abstract BindingSource BindingSource { get; }
/// <summary>
/// Gets a value indicating whether or not to convert an empty string value or one containing only whitespace
/// characters to <c>null</c> when representing a model as text.
/// </summary>
public abstract bool ConvertEmptyStringToNull { get; }
/// <summary>
/// Gets the name of the model's datatype. Overrides <see cref="ModelType"/> in some
/// display scenarios.
/// </summary>
/// <value><c>null</c> unless set manually or through additional metadata e.g. attributes.</value>
public abstract string DataTypeName { get; }
/// <summary>
/// Gets the description of the model.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Gets the format string (see https://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to display the
/// model.
/// </summary>
public abstract string DisplayFormatString { get; }
/// <summary>
/// Gets the display name of the model.
/// </summary>
public abstract string DisplayName { get; }
/// <summary>
/// Gets the format string (see https://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to edit the model.
/// </summary>
public abstract string EditFormatString { get; }
/// <summary>
/// Gets the <see cref="ModelMetadata"/> for elements of <see cref="ModelType"/> if that <see cref="Type"/>
/// implements <see cref="IEnumerable"/>.
/// </summary>
/// <value>
/// <see cref="ModelMetadata"/> for <c>T</c> if <see cref="ModelType"/> implements
/// <see cref="IEnumerable{T}"/>. <see cref="ModelMetadata"/> for <c>object</c> if <see cref="ModelType"/>
/// implements <see cref="IEnumerable"/> but not <see cref="IEnumerable{T}"/>. <c>null</c> otherwise i.e. when
/// <see cref="IsEnumerableType"/> is <c>false</c>.
/// </value>
public abstract ModelMetadata ElementMetadata { get; }
/// <summary>
/// Gets the ordered and grouped display names and values of all <see cref="Enum"/> values in
/// <see cref="UnderlyingOrModelType"/>.
/// </summary>
/// <value>
/// An <see cref="IEnumerable{T}"/> of <see cref="KeyValuePair{EnumGroupAndName, String}"/> of mappings between
/// <see cref="Enum"/> field groups, names and values. <c>null</c> if <see cref="IsEnum"/> is <c>false</c>.
/// </value>
public abstract IEnumerable<KeyValuePair<EnumGroupAndName, string>> EnumGroupedDisplayNamesAndValues { get; }
/// <summary>
/// Gets the names and values of all <see cref="Enum"/> values in <see cref="UnderlyingOrModelType"/>.
/// </summary>
/// <value>
/// An <see cref="IReadOnlyDictionary{String, String}"/> of mappings between <see cref="Enum"/> field names
/// and values. <c>null</c> if <see cref="IsEnum"/> is <c>false</c>.
/// </value>
public abstract IReadOnlyDictionary<string, string> EnumNamesAndValues { get; }
/// <summary>
/// Gets a value indicating whether <see cref="EditFormatString"/> has a non-<c>null</c>, non-empty
/// value different from the default for the datatype.
/// </summary>
public abstract bool HasNonDefaultEditFormat { get; }
/// <summary>
/// Gets a value indicating whether the value should be HTML-encoded.
/// </summary>
/// <value>If <c>true</c>, value should be HTML-encoded. Default is <c>true</c>.</value>
public abstract bool HtmlEncode { get; }
/// <summary>
/// Gets a value indicating whether the "HiddenInput" display template should return
/// <c>string.Empty</c> (not the expression value) and whether the "HiddenInput" editor template should not
/// also return the expression value (together with the hidden <input> element).
/// </summary>
/// <remarks>
/// If <c>true</c>, also causes the default <see cref="object"/> display and editor templates to return HTML
/// lacking the usual per-property <div> wrapper around the associated property. Thus the default
/// <see cref="object"/> display template effectively skips the property and the default <see cref="object"/>
/// editor template returns only the hidden <input> element for the property.
/// </remarks>
public abstract bool HideSurroundingHtml { get; }
/// <summary>
/// Gets a value indicating whether or not the model value can be bound by model binding. This is only
/// applicable when the current instance represents a property.
/// </summary>
/// <remarks>
/// If <c>true</c> then the model value is considered supported by model binding and can be set
/// based on provided input in the request.
/// </remarks>
public abstract bool IsBindingAllowed { get; }
/// <summary>
/// Gets a value indicating whether or not the model value is required by model binding. This is only
/// applicable when the current instance represents a property.
/// </summary>
/// <remarks>
/// If <c>true</c> then the model value is considered required by model binding and must have a value
/// supplied in the request to be considered valid.
/// </remarks>
public abstract bool IsBindingRequired { get; }
/// <summary>
/// Gets a value indicating whether <see cref="UnderlyingOrModelType"/> is for an <see cref="Enum"/>.
/// </summary>
/// <value>
/// <c>true</c> if <c>type.IsEnum</c> (<c>type.GetTypeInfo().IsEnum</c> for DNX Core 5.0) is <c>true</c> for
/// <see cref="UnderlyingOrModelType"/>; <c>false</c> otherwise.
/// </value>
public abstract bool IsEnum { get; }
/// <summary>
/// Gets a value indicating whether <see cref="UnderlyingOrModelType"/> is for an <see cref="Enum"/> with an
/// associated <see cref="FlagsAttribute"/>.
/// </summary>
/// <value>
/// <c>true</c> if <see cref="IsEnum"/> is <c>true</c> and <see cref="UnderlyingOrModelType"/> has an
/// associated <see cref="FlagsAttribute"/>; <c>false</c> otherwise.
/// </value>
public abstract bool IsFlagsEnum { get; }
/// <summary>
/// Gets a value indicating whether or not the model value is read-only. This is only applicable when
/// the current instance represents a property.
/// </summary>
public abstract bool IsReadOnly { get; }
/// <summary>
/// Gets a value indicating whether or not the model value is required. This is only applicable when
/// the current instance represents a property.
/// </summary>
/// <remarks>
/// <para>
/// If <c>true</c> then the model value is considered required by validators.
/// </para>
/// <para>
/// By default an implicit <c>System.ComponentModel.DataAnnotations.RequiredAttribute</c> will be added
/// if not present when <c>true.</c>.
/// </para>
/// </remarks>
public abstract bool IsRequired { get; }
/// <summary>
/// Gets the <see cref="IModelBindingMessageProvider"/> instance.
/// </summary>
public abstract IModelBindingMessageProvider ModelBindingMessageProvider { get; }
/// <summary>
/// Gets a value indicating where the current metadata should be ordered relative to other properties
/// in its containing type.
/// </summary>
/// <remarks>
/// <para>For example this property is used to order items in <see cref="Properties"/>.</para>
/// <para>The default order is <c>10000</c>.</para>
/// </remarks>
/// <value>The order value of the current metadata.</value>
public abstract int Order { get; }
/// <summary>
/// Gets the text to display as a placeholder value for an editor.
/// </summary>
public abstract string Placeholder { get; }
/// <summary>
/// Gets the text to display when the model is <c>null</c>.
/// </summary>
public abstract string NullDisplayText { get; }
/// <summary>
/// Gets the <see cref="IPropertyFilterProvider"/>, which can determine which properties
/// should be model bound.
/// </summary>
public abstract IPropertyFilterProvider PropertyFilterProvider { get; }
/// <summary>
/// Gets a value that indicates whether the property should be displayed in read-only views.
/// </summary>
public abstract bool ShowForDisplay { get; }
/// <summary>
/// Gets a value that indicates whether the property should be displayed in editable views.
/// </summary>
public abstract bool ShowForEdit { get; }
/// <summary>
/// Gets a value which is the name of the property used to display the model.
/// </summary>
public abstract string SimpleDisplayProperty { get; }
/// <summary>
/// Gets a string used by the templating system to discover display-templates and editor-templates.
/// </summary>
public abstract string TemplateHint { get; }
/// <summary>
/// Gets an <see cref="IShouldValidate"/> implementation that indicates whether this model should be validated.
/// If <c>null</c>, properties with this <see cref="ModelMetadata"/> are validated.
/// </summary>
/// <value>Defaults to <c>null</c>.</value>
public abstract IShouldValidate ShouldValidate { get; }
/// <summary>
/// Gets a value that indicates whether properties or elements of the model should be validated.
/// </summary>
public abstract bool ValidateChildren { get; }
/// <summary>
/// Gets a collection of metadata items for validators.
/// </summary>
public abstract IReadOnlyList<object> ValidatorMetadata { get; }
/// <summary>
/// Gets the <see cref="Type"/> for elements of <see cref="ModelType"/> if that <see cref="Type"/>
/// implements <see cref="IEnumerable"/>.
/// </summary>
public Type ElementType { get; private set; }
/// <summary>
/// Gets a value indicating whether <see cref="ModelType"/> is a simple type.
/// </summary>
/// <remarks>
/// A simple type is defined as a <see cref="Type"/> which has a
/// <see cref="System.ComponentModel.TypeConverter"/> that can convert from <see cref="string"/>.
/// </remarks>
public bool IsComplexType { get; private set; }
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> is a <see cref="Nullable{T}"/>.
/// </summary>
public bool IsNullableValueType { get; private set; }
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> is a collection type.
/// </summary>
/// <remarks>
/// A collection type is defined as a <see cref="Type"/> which is assignable to <see cref="ICollection{T}"/>.
/// </remarks>
public bool IsCollectionType { get; private set; }
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> is an enumerable type.
/// </summary>
/// <remarks>
/// An enumerable type is defined as a <see cref="Type"/> which is assignable to
/// <see cref="IEnumerable"/>, and is not a <see cref="string"/>.
/// </remarks>
public bool IsEnumerableType { get; private set; }
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> allows <c>null</c> values.
/// </summary>
public bool IsReferenceOrNullableType { get; private set; }
/// <summary>
/// Gets the underlying type argument if <see cref="ModelType"/> inherits from <see cref="Nullable{T}"/>.
/// Otherwise gets <see cref="ModelType"/>.
/// </summary>
/// <remarks>
/// Identical to <see cref="ModelType"/> unless <see cref="IsNullableValueType"/> is <c>true</c>.
/// </remarks>
public Type UnderlyingOrModelType { get; private set; }
/// <summary>
/// Gets a property getter delegate to get the property value from a model object.
/// </summary>
public abstract Func<object, object> PropertyGetter { get; }
/// <summary>
/// Gets a property setter delegate to set the property value on a model object.
/// </summary>
public abstract Action<object, object> PropertySetter { get; }
/// <summary>
/// Gets a display name for the model.
/// </summary>
/// <remarks>
/// <see cref="GetDisplayName()"/> will return the first of the following expressions which has a
/// non-<c>null</c> value: <c>DisplayName</c>, <c>PropertyName</c>, <c>ModelType.Name</c>.
/// </remarks>
/// <returns>The display name.</returns>
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()
{
// Normaly caching the hashcode would be dangerous, but Identity is deeply immutable so this is safe.
if (_hashCode == null)
{
_hashCode = Identity.GetHashCode();
}
return _hashCode.Value;
}
private void InitializeTypeInformation()
{
Debug.Assert(ModelType != null);
IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string));
IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null;
IsReferenceOrNullableType = !ModelType.GetTypeInfo().IsValueType || IsNullableValueType;
UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType;
var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>));
IsCollectionType = collectionType != null;
if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType))
{
// Do nothing, not Enumerable.
}
else if (ModelType.IsArray)
{
IsEnumerableType = true;
ElementType = ModelType.GetElementType();
}
else
{
IsEnumerableType = true;
var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable<>));
ElementType = enumerableType?.GenericTypeArguments[0];
if (ElementType == null)
{
// ModelType implements IEnumerable but not IEnumerable<T>.
ElementType = typeof(object);
}
Debug.Assert(
ElementType != null,
$"Unable to find element type for '{ModelType.FullName}' though IsEnumerableType is true.");
}
}
private string DebuggerToString()
{
if (Identity.MetadataKind == ModelMetadataKind.Type)
{
return $"ModelMetadata (Type: '{ModelType.Name}')";
}
else
{
return $"ModelMetadata (Property: '{ContainerType.Name}.{PropertyName}' Type: '{ModelType.Name}')";
}
}
}
}