Skip to content

Commit 78e5536

Browse files
committed
Merge branch 'improvement/ItemConfiguration-refactoring' into develop
Refactored the `ItemConfiguration` class to be a property of `ItemMetadata`. Since the vast majority of the properties on `ItemConfiguration` don't change at runtime (since they map to `[Attribute]` values), it makes sense to evaluate them once and then cache them with their corresponding `ItemMetadata` instance. This also allows us to "flip the script" on how we pass metadata within the mapping services: Previously, we passed an `ItemConfiguration` (or `PropertyConfiguration`) instance, through which recipients could access the `ItemMetadata` (or `PropertyAccessor`); now, the `ItemMetadata` (or `PropertyAccessor`) is relayed within the mapping services, through which recipients can access the `ItemConfiguration` property. In practice, this is a more intuitive data model, and should be faster. The one element of `ItemConfiguration` that did change at runtime was the `attributePrefix`, which was previously factored into the `ItemConfiguration.AttributeKey` property. Since this can't be factored into the cached `ItemMetadata.Configuration` reference, it must be relayed independently within the mapping services. This has been added to most of the method signatures within `TopicMappingService` and `ReverseTopicMappingService` (and its dependent `BindingModelValidator`). Further, a new `GetCompositeAttributeKey()` convenience method was introduced to centralize how the concatenation of the `attributePrefix` and `AttributeKey` should work. Finally, this allowed us to remove the `PropertyConfiguration` entirely. The one remaining dependency on it—the `Validate` method—has been moved to `MemberAccessor`.
2 parents 65f6414 + 4b3a6d6 commit 78e5536

8 files changed

Lines changed: 237 additions & 228 deletions

File tree

OnTopic.AspNetCore.Mvc.Tests/ErrorControllerTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public ErrorControllerTest(TestTopicRepository topicRepository) {
5858
| TEST: ERROR CONTROLLER: HTTP: RETURNS EXPECTED ERROR
5959
\-------------------------------------------------------------------------------------------------------------------------*/
6060
/// <summary>
61-
/// Triggers the <see cref="ErrorController.HttpAsync(Int32)" /> action with different status codes, and ensures that the
62-
/// expected <see cref="Topic"/> is returned in the <see cref="TopicViewResult"/>.
61+
/// Triggers the <see cref="ErrorController.HttpAsync(Int32, Boolean)" /> action with different status codes, and ensures
62+
/// that the expected <see cref="Topic"/> is returned in the <see cref="TopicViewResult"/>.
6363
/// </summary>
6464
[Theory]
6565
[InlineData(405, "405")] // Exact match

OnTopic/Internal/Reflection/ItemMetadata.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using System.Collections;
77
using System.Reflection;
88
using OnTopic.Attributes;
9+
using OnTopic.Mapping;
10+
using OnTopic.Mapping.Internal;
11+
using OnTopic.Mapping.Reverse;
912

1013
namespace OnTopic.Internal.Reflection {
1114

@@ -24,6 +27,7 @@ internal abstract class ItemMetadata {
2427
private readonly ICustomAttributeProvider _attributeProvider;
2528
private readonly Type _type = default!;
2629
private List<Attribute> _customAttributes = default!;
30+
private ItemConfiguration? _itemConfiguration;
2731

2832
/*==========================================================================================================================
2933
| CONSTRUCTOR
@@ -102,6 +106,26 @@ bool isList()
102106
}
103107
}
104108

109+
/*==========================================================================================================================
110+
| CONFIGURATION
111+
\-------------------------------------------------------------------------------------------------------------------------*/
112+
/// <summary>
113+
/// Gets a reference to the configuration settings for the current item, based on the annotations configured in the code.
114+
/// </summary>
115+
/// <remarks>
116+
/// The <see cref="ItemConfiguration"/> class identifies annotations used by the <see cref="TopicMappingService"/> and the
117+
/// <see cref="ReverseTopicMappingService"/>. While it can be called on any <see cref="Type"/>, it is only expected to
118+
/// offer a benefit for model classes intended for mapping.
119+
/// </remarks>
120+
internal ItemConfiguration Configuration {
121+
get {
122+
if (_itemConfiguration is null) {
123+
_itemConfiguration = new(this);
124+
}
125+
return _itemConfiguration;
126+
}
127+
}
128+
105129
/*==========================================================================================================================
106130
| IS NULLABLE?
107131
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic/Internal/Reflection/MemberAccessor.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
| Client Ignia, LLC
44
| Project Topics Library
55
\=============================================================================================================================*/
6+
using System.ComponentModel.DataAnnotations;
67
using System.Reflection;
78
using OnTopic.Attributes;
89

@@ -218,6 +219,20 @@ internal void SetValue(object target, object? value, bool allowConversion = fals
218219

219220
}
220221

222+
/*==========================================================================================================================
223+
| METHOD: VALIDATE
224+
\-------------------------------------------------------------------------------------------------------------------------*/
225+
/// <summary>
226+
/// Given a target DTO, will automatically identify any attributes that derive from <see cref="ValidationAttribute"/> and
227+
/// ensure that their conditions are satisfied.
228+
/// </summary>
229+
/// <param name="target">The target DTO to validate the current property on.</param>
230+
internal void Validate(object target) {
231+
foreach (ValidationAttribute validator in CustomAttributes.OfType<ValidationAttribute>()) {
232+
validator.Validate(GetValue(target), Name);
233+
}
234+
}
235+
221236
/*==========================================================================================================================
222237
| IS VALID?
223238
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic/Mapping/Internal/ItemConfiguration.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ namespace OnTopic.Mapping.Internal {
3131
/// then the <see cref="ITopicMappingService"/> will instead use the value defined by that attribute, thus allowing a
3232
/// property on the DTO to be aliased to a different property or attribute name on the source <see cref="Topic"/>.
3333
/// </para>
34-
/// <para>
35-
/// The <see cref="ItemConfiguration"/> works with both <see cref="ParameterInfo"/> and <see cref="PropertyInfo"/>
36-
/// instances, whereas the <see cref="PropertyConfiguration"/> works exclusively with <see cref="PropertyInfo"/> instances.
37-
/// </para>
3834
/// </remarks>
3935
internal class ItemConfiguration {
4036

@@ -48,21 +44,18 @@ internal class ItemConfiguration {
4844
/// <param name="itemMetadata">
4945
/// The <see cref="ItemMetadata"/> instance associated with this <see cref="ItemConfiguration"/>.
5046
/// </param>
51-
/// <param name="name">The name of the <see cref="ParameterInfo"/> or <see cref="PropertyInfo"/>.</param>
52-
/// <param name="attributePrefix">The prefix to apply to the attributes.</param>
53-
internal ItemConfiguration(ItemMetadata itemMetadata, string name, string? attributePrefix = "") {
47+
internal ItemConfiguration(ItemMetadata itemMetadata) {
5448

5549
/*------------------------------------------------------------------------------------------------------------------------
5650
| Set backing properties
5751
\-----------------------------------------------------------------------------------------------------------------------*/
58-
Metadata = itemMetadata;
5952
CustomAttributes = itemMetadata.CustomAttributes;
6053

6154
/*------------------------------------------------------------------------------------------------------------------------
6255
| Set default values
6356
\-----------------------------------------------------------------------------------------------------------------------*/
64-
AttributeKey = attributePrefix + name;
65-
AttributePrefix = attributePrefix;
57+
AttributeKey = itemMetadata.Name;
58+
AttributePrefix = "";
6659
DefaultValue = null;
6760
InheritValue = false;
6861
CollectionKey = AttributeKey;
@@ -79,9 +72,9 @@ internal ItemConfiguration(ItemMetadata itemMetadata, string name, string? attri
7972
GetAttributeValue<MapAsAttribute>( a => MapAs = a.Type);
8073
GetAttributeValue<DefaultValueAttribute>( a => DefaultValue = a.Value);
8174
GetAttributeValue<InheritAttribute>( a => InheritValue = true);
82-
GetAttributeValue<AttributeKeyAttribute>( a => AttributeKey = attributePrefix + a.Key);
75+
GetAttributeValue<AttributeKeyAttribute>( a => AttributeKey = a.Key);
8376
GetAttributeValue<MapToParentAttribute>( a => MapToParent = true);
84-
GetAttributeValue<MapToParentAttribute>( a => AttributePrefix += (a.AttributePrefix?? name));
77+
GetAttributeValue<MapToParentAttribute>( a => AttributePrefix += (a.AttributePrefix?? AttributeKey));
8578
GetAttributeValue<IncludeAttribute>( a => IncludeAssociations = a.Associations);
8679
GetAttributeValue<FlattenAttribute>( a => FlattenChildren = true);
8780
GetAttributeValue<MetadataAttribute>( a => MetadataKey = a.Key);
@@ -114,14 +107,6 @@ internal ItemConfiguration(ItemMetadata itemMetadata, string name, string? attri
114107

115108
}
116109

117-
/*==========================================================================================================================
118-
| PROPERTY: METADATA
119-
\-------------------------------------------------------------------------------------------------------------------------*/
120-
/// <summary>
121-
/// The <see cref="ItemMetadata"/> that the current <see cref="ItemConfiguration"/> is associated with.
122-
/// </summary>
123-
internal ItemMetadata Metadata { get; }
124-
125110
/*==========================================================================================================================
126111
| PROPERTY: CUSTOM ATTRIBUTES
127112
\-------------------------------------------------------------------------------------------------------------------------*/
@@ -433,6 +418,21 @@ internal ItemConfiguration(ItemMetadata itemMetadata, string name, string? attri
433418
/// </remarks>
434419
internal Dictionary<string, string> AttributeFilters { get; }
435420

421+
/*==========================================================================================================================
422+
| METHOD: GET COMPOSITE ATTRIBUTE KEY
423+
\-------------------------------------------------------------------------------------------------------------------------*/
424+
/// <summary>
425+
/// Retrieves the current <see cref="AttributeKey"/>, and, optionally, the supplied <paramref name="attributePrefix"/>.
426+
/// </summary>
427+
/// <param name="attributePrefix">
428+
/// The current mapping operation's attribute prefix to be prepended to the <see cref="AttributeKey"/>.
429+
/// </param>
430+
/// <returns>
431+
/// The composite attribute key, based on <see cref="AttributeKey"/> and, optionally, the supplied <paramref name="
432+
/// attributePrefix"/>.
433+
/// </returns>
434+
internal string GetCompositeAttributeKey(string? attributePrefix) => $"{attributePrefix}{AttributeKey}";
435+
436436
/*==========================================================================================================================
437437
| METHOD: SATISFIES ATTRIBUTE FILTERS
438438
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic/Mapping/Internal/PropertyConfiguration.cs

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)