Skip to content

Commit a82f7e6

Browse files
committed
Merge branch 'feature/MemberAccessor' into develop
Major refactoring of the internal `OnTopic.Internal.Reflection` classes. Previously, these classes relied on the .NET Reflection libraries to dynamically create delegate signatures for properties and methods before getting or setting values. Now, the delegate signature is created by the new `MemberAccessor` class and cached as part of a `TypeAccessorCache`. This promises to improve performance. As part of this, I significantly refactored the organization and approach to the code. `TypeMemberInfoCollection` has been replaced by `TypeAccessorCache`, `MemberInfoCollection<T>` has been replaced by `TypeAccessor`, and `MemberDispatcher` has been replaced by `MemberAccessor`. This takes a more intuitive top-down approach, where as previously `MemberDispatcher` maintained its own `TypeMemberInfoCollection` cache which complicated the relationship between objects. I also improved the validation of accessors, thus helping prevent unnecessary conversions, or attempts to set members that can't be set (e.g., by setting `null` to a non-nullable property). In theory, this is supposed to have a major performance benefit—and, indeed, according to my initial testing directly against the new and old methods, it did. In real-world testing, however, we're not seeing that much benefit. That said, this also isn't any slower, and while it adds some complexity when calculating the delegate signatures, it's also much better organized. This resolves Issue #90.
2 parents 34084fd + c084fe5 commit a82f7e6

19 files changed

Lines changed: 2132 additions & 1352 deletions

OnTopic.AspNetCore.Mvc/TopicViewResultExecutor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
using Microsoft.AspNetCore.Mvc.Razor;
1616
using Microsoft.AspNetCore.Mvc.ViewEngines;
1717
using Microsoft.AspNetCore.Mvc.ViewFeatures;
18-
using Microsoft.Extensions.Logging;
1918
using Microsoft.Extensions.Options;
2019
using OnTopic.Internal.Diagnostics;
2120

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
using OnTopic.Data.Caching;
12+
using OnTopic.Internal.Reflection;
13+
using OnTopic.Lookup;
14+
using OnTopic.Mapping;
15+
using OnTopic.Repositories;
16+
using OnTopic.TestDoubles;
17+
using OnTopic.Tests.TestDoubles;
18+
using OnTopic.ViewModels;
19+
20+
namespace OnTopic.Tests.Fixtures {
21+
22+
/*============================================================================================================================
23+
| CLASS: TYPE ACCESSOR FIXTURE
24+
\---------------------------------------------------------------------------------------------------------------------------*/
25+
/// <summary>
26+
/// Introduces a shared context to use for unit tests depending on an <see cref="TypeAccessor"/>.
27+
/// </summary>
28+
public class TypeAccessorFixture<T> {
29+
30+
/*==========================================================================================================================
31+
| CONSTRUCTOR
32+
\-------------------------------------------------------------------------------------------------------------------------*/
33+
public TypeAccessorFixture() {
34+
35+
/*------------------------------------------------------------------------------------------------------------------------
36+
| Create type accessor
37+
\-----------------------------------------------------------------------------------------------------------------------*/
38+
TypeAccessor = new TypeAccessor(typeof(T));
39+
40+
}
41+
42+
/*==========================================================================================================================
43+
| TYPE ACCESSOR
44+
\-------------------------------------------------------------------------------------------------------------------------*/
45+
/// <summary>
46+
/// A <see cref="TypeAccessor"/> for accessing <see cref="MemberAccessor"/> instances.
47+
/// </summary>
48+
internal TypeAccessor TypeAccessor { get; private set; }
49+
50+
}
51+
}

OnTopic.Tests/MemberAccessorTest.cs

Lines changed: 410 additions & 0 deletions
Large diffs are not rendered by default.

OnTopic.Tests/TopicMappingServiceTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,11 +1463,11 @@ public async Task Map_CachedTopic_ReturnsUniqueReferencePerType() {
14631463

14641464
var topic = new Topic("Test", "Filtered", null, 5);
14651465

1466-
var target1 = (FilteredTopicViewModel?)await cachedMappingService.MapAsync<FilteredTopicViewModel>(topic).ConfigureAwait(false);
1467-
var target2 = (FilteredTopicViewModel?)await cachedMappingService.MapAsync<FilteredTopicViewModel>(topic).ConfigureAwait(false);
1466+
var target1 = await cachedMappingService.MapAsync<FilteredTopicViewModel>(topic).ConfigureAwait(false);
1467+
var target2 = await cachedMappingService.MapAsync<FilteredTopicViewModel>(topic).ConfigureAwait(false);
14681468
var target3 = (TopicViewModel?)await cachedMappingService.MapAsync<PageTopicViewModel>(topic).ConfigureAwait(false);
14691469

1470-
Assert.Equal<FilteredTopicViewModel?>(target1, target2);
1470+
Assert.Equal(target1, target2);
14711471
Assert.NotEqual<object?>(target1, target3);
14721472

14731473
}

OnTopic.Tests/TopicReferenceCollectionTest.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,8 @@ public void Add_TopicReferenceWithBusinessLogic_RemovedReference() {
439439
| TEST: ADD: TOPIC REFERENCE WITH BUSINESS LOGIC: THROWS EXCEPTION
440440
\-------------------------------------------------------------------------------------------------------------------------*/
441441
/// <summary>
442-
/// Sets a topic reference on a topic instance with an invalid value; ensures an exception is thrown.
442+
/// Sets a topic reference on a topic instance with an invalid value; ensures an <see cref="ArgumentOutOfRangeException"/>
443+
/// is thrown.
443444
/// </summary>
444445
[Fact]
445446
public void Add_TopicReferenceWithBusinessLogic_ThrowsException() {
@@ -457,7 +458,8 @@ public void Add_TopicReferenceWithBusinessLogic_ThrowsException() {
457458
| TEST: SET: TOPIC REFERENCE WITH BUSINESS LOGIC: THROWS EXCEPTION
458459
\-------------------------------------------------------------------------------------------------------------------------*/
459460
/// <summary>
460-
/// Sets a topic reference on a topic instance with an invalid value; ensures an exception is thrown.
461+
/// Sets a topic reference on a topic instance with an invalid value; ensures an <see cref="ArgumentOutOfRangeException"/>
462+
/// is thrown.
461463
/// </summary>
462464
[Fact]
463465
public void Set_TopicReferenceWithBusinessLogic_ThrowsException() {

0 commit comments

Comments
 (0)