55\=============================================================================================================================*/
66using System ;
77using System . Collections ;
8- using System . Collections . Concurrent ;
98using System . Collections . Generic ;
109using System . Diagnostics . CodeAnalysis ;
1110using System . Linq ;
1211using System . Reflection ;
1312using System . Threading . Tasks ;
1413using OnTopic . Attributes ;
14+ using OnTopic . Internal . Collections ;
1515using OnTopic . Internal . Diagnostics ;
1616using OnTopic . Internal . Mapping ;
1717using OnTopic . Internal . Reflection ;
@@ -90,7 +90,7 @@ public TopicMappingService(ITopicRepository topicRepository, ITypeLookupService
9090 private async Task < object ? > MapAsync (
9191 Topic ? topic ,
9292 Relationships relationships ,
93- ConcurrentDictionary < int , object > cache ,
93+ MappedTopicCache cache ,
9494 string ? attributePrefix = null
9595 ) {
9696
@@ -106,23 +106,32 @@ public TopicMappingService(ITopicRepository topicRepository, ITypeLookupService
106106 /*------------------------------------------------------------------------------------------------------------------------
107107 | Handle cached objects
108108 \-----------------------------------------------------------------------------------------------------------------------*/
109- if ( cache . TryGetValue ( topic . Id , out var dto ) ) {
110- return dto ;
109+ object ? target ;
110+
111+ if ( cache . TryGetValue ( topic . Id , out var cacheEntry ) ) {
112+ target = cacheEntry . MappedTopic ;
113+ if ( cacheEntry . GetMissingRelationships ( relationships ) == Relationships . None ) {
114+ return target ;
115+ }
111116 }
112117
113118 /*------------------------------------------------------------------------------------------------------------------------
114119 | Instantiate object
115120 \-----------------------------------------------------------------------------------------------------------------------*/
116- var viewModelType = _typeLookupService . Lookup ( $ " { topic . ContentType } TopicViewModel" ) ;
121+ else {
117122
118- if ( viewModelType is null || ! viewModelType . Name . EndsWith ( "TopicViewModel" , StringComparison . CurrentCultureIgnoreCase ) ) {
119- throw new InvalidOperationException (
120- $ "No class named '{ topic . ContentType } TopicViewModel' could be located in any loaded assemblies. This is required " +
121- $ "to map the topic '{ topic . GetUniqueKey ( ) } '."
122- ) ;
123- }
123+ var viewModelType = _typeLookupService . Lookup ( $ "{ topic . ContentType } TopicViewModel") ;
124124
125- var target = Activator . CreateInstance ( viewModelType ) ;
125+ if ( viewModelType is null || ! viewModelType . Name . EndsWith ( "TopicViewModel" , StringComparison . CurrentCultureIgnoreCase ) ) {
126+ throw new InvalidOperationException (
127+ $ "No class named '{ topic . ContentType } TopicViewModel' could be located in any loaded assemblies. This is required " +
128+ $ "to map the topic '{ topic . GetUniqueKey ( ) } '."
129+ ) ;
130+ }
131+
132+ target = Activator . CreateInstance ( viewModelType ) ;
133+
134+ }
126135
127136 /*------------------------------------------------------------------------------------------------------------------------
128137 | Provide mapping
@@ -168,10 +177,10 @@ public TopicMappingService(ITopicRepository topicRepository, ITypeLookupService
168177 /// The target view model with the properties appropriately mapped.
169178 /// </returns>
170179 private async Task < object > MapAsync (
171- Topic ? topic ,
172- object target ,
173- Relationships relationships ,
174- ConcurrentDictionary < int , object > cache ,
180+ Topic ? topic ,
181+ object target ,
182+ Relationships relationships ,
183+ MappedTopicCache cache ,
175184 string ? attributePrefix = null
176185 ) {
177186
@@ -193,20 +202,34 @@ private async Task<object> MapAsync(
193202
194203 /*------------------------------------------------------------------------------------------------------------------------
195204 | Handle cached objects
196- \-----------------------------------------------------------------------------------------------------------------------*/
197- if ( cache . TryGetValue ( topic . Id , out var dto ) ) {
198- return dto ;
205+ >-------------------------------------------------------------------------------------------------------------------------
206+ | If the cache contains an entry, check to make sure it includes all of the requested relationships. If it does, return
207+ | it. If it doesn't, determine the missing relationships and request to have those mapped.
208+ \-----------------------------------------------------------------------------------------------------------------------*/
209+ if ( cache . TryGetValue ( topic . Id , out var cacheEntry ) ) {
210+ relationships = cacheEntry . GetMissingRelationships ( relationships ) ;
211+ target = cacheEntry . MappedTopic ;
212+ if ( relationships == Relationships . None ) {
213+ return cacheEntry . MappedTopic ;
214+ }
215+ cacheEntry . AddMissingRelationships ( relationships ) ;
199216 }
200217 else if ( ! topic . IsNew ) {
201- cache . GetOrAdd ( topic . Id , target ) ;
218+ cache . GetOrAdd (
219+ topic . Id ,
220+ new MappedTopicCacheEntry ( ) {
221+ MappedTopic = target ,
222+ Relationships = relationships
223+ }
224+ ) ;
202225 }
203226
204227 /*------------------------------------------------------------------------------------------------------------------------
205228 | Loop through properties, mapping each one
206229 \-----------------------------------------------------------------------------------------------------------------------*/
207230 var taskQueue = new List < Task > ( ) ;
208231 foreach ( var property in _typeCache . GetMembers < PropertyInfo > ( target . GetType ( ) ) ) {
209- taskQueue . Add ( SetPropertyAsync ( topic , target , relationships , property , cache , attributePrefix ) ) ;
232+ taskQueue . Add ( SetPropertyAsync ( topic , target , relationships , property , cache , attributePrefix , cacheEntry != null ) ) ;
210233 }
211234 await Task . WhenAll ( taskQueue . ToArray ( ) ) . ConfigureAwait ( false ) ;
212235
@@ -230,13 +253,15 @@ private async Task<object> MapAsync(
230253 /// <param name="property">Information related to the current property.</param>
231254 /// <param name="cache">A cache to keep track of already-mapped object instances.</param>
232255 /// <param name="attributePrefix">The prefix to apply to the attributes.</param>
256+ /// <param name="mapRelationshipsOnly">Determines if properties not associated with properties should be mapped.</param>
233257 protected async Task SetPropertyAsync (
234- Topic source ,
235- object target ,
236- Relationships relationships ,
237- PropertyInfo property ,
238- ConcurrentDictionary < int , object > cache ,
239- string ? attributePrefix = null
258+ Topic source ,
259+ object target ,
260+ Relationships relationships ,
261+ PropertyInfo property ,
262+ MappedTopicCache cache ,
263+ string ? attributePrefix = null ,
264+ bool mapRelationshipsOnly = false
240265 ) {
241266
242267 /*------------------------------------------------------------------------------------------------------------------------
@@ -261,7 +286,7 @@ protected async Task SetPropertyAsync(
261286 /*------------------------------------------------------------------------------------------------------------------------
262287 | Assign default value
263288 \-----------------------------------------------------------------------------------------------------------------------*/
264- if ( configuration . DefaultValue is not null ) {
289+ if ( ! mapRelationshipsOnly && configuration . DefaultValue is not null ) {
265290 property . SetValue ( target , configuration . DefaultValue ) ;
266291 }
267292
@@ -274,7 +299,7 @@ protected async Task SetPropertyAsync(
274299 else if ( SetCompatibleProperty ( source , target , configuration ) ) {
275300 //Performed 1:1 mapping between source and target
276301 }
277- else if ( _typeCache . HasSettableProperty ( target . GetType ( ) , property . Name ) ) {
302+ else if ( ! mapRelationshipsOnly && _typeCache . HasSettableProperty ( target . GetType ( ) , property . Name ) ) {
278303 SetScalarValue ( source , target , configuration ) ;
279304 }
280305 else if ( typeof ( IList ) . IsAssignableFrom ( property . PropertyType ) ) {
@@ -398,11 +423,11 @@ protected static void SetScalarValue(Topic source, object target, PropertyConfig
398423 /// </param>
399424 /// <param name="cache">A cache to keep track of already-mapped object instances.</param>
400425 protected async Task SetCollectionValueAsync (
401- Topic source ,
402- object target ,
403- Relationships relationships ,
404- PropertyConfiguration configuration ,
405- ConcurrentDictionary < int , object > cache
426+ Topic source ,
427+ object target ,
428+ Relationships relationships ,
429+ PropertyConfiguration configuration ,
430+ MappedTopicCache cache
406431 ) {
407432
408433 /*------------------------------------------------------------------------------------------------------------------------
@@ -590,10 +615,10 @@ IList<Topic> GetRelationship(RelationshipType relationship, Func<string, bool> c
590615 /// </param>
591616 /// <param name="cache">A cache to keep track of already-mapped object instances.</param>
592617 protected async Task PopulateTargetCollectionAsync (
593- IList < Topic > sourceList ,
594- IList targetList ,
595- PropertyConfiguration configuration ,
596- ConcurrentDictionary < int , object > cache
618+ IList < Topic > sourceList ,
619+ IList targetList ,
620+ PropertyConfiguration configuration ,
621+ MappedTopicCache cache
597622 ) {
598623
599624 /*------------------------------------------------------------------------------------------------------------------------
@@ -698,10 +723,10 @@ void AddToList(object dto) {
698723 /// </param>
699724 /// <param name="cache">A cache to keep track of already-mapped object instances.</param>
700725 protected async Task SetTopicReferenceAsync (
701- Topic source ,
702- object target ,
703- PropertyConfiguration configuration ,
704- ConcurrentDictionary < int , object > cache
726+ Topic source ,
727+ object target ,
728+ PropertyConfiguration configuration ,
729+ MappedTopicCache cache
705730 ) {
706731
707732 /*------------------------------------------------------------------------------------------------------------------------
0 commit comments