You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generally, local caching is not an optimal solution; a decorator is much preferred. That said, the `CachedTopicMappingService` decorator is ineffective in this particular case because the `LayoutControllerBase{T}` manually creates children. As a result, instead of the entire view model _graph_ being cached, each individual _item_ is cached.
This introduces undesirable behavior when those view model graphs overlap, as e.g. the `INavigationTopicViewModel<T>` for `Menu` might end up with additional children from the `INavigationTopicViewModel<T>` for `PageLevelNavigation`. This is generally not a problem for mapping, but `LayoutControllerBase{}` needs tighter control over the boundaries of its edges than most applications (where unnecessary spillage would not interfere with the functionality).
To mitigate this, introduces the `CachedLayoutControllerBase{T}`.
To better differentiate between the methods, renamed `AddNestedTopicsAsync()` to `GetViewModelAsync()`, and introduced a new "bootstrap" method, `GetRootModelAsync()`, which kickstarts the process, thus allowing implementers, such as the `CachedLayoutControllerBase<T>`, to differentiate between the root view model, and its descendents.
Also updated documentation to call out this behavior.
Copy file name to clipboardExpand all lines: Ignia.Topics.Web.Mvc/README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -24,6 +24,7 @@ There are six main controllers that ship with the MVC implementation. In additio
24
24
-**`ErrorControllerBase<T>`**: Provides support for `Error`, `NotFound`, and `InternalServer` actions. Can accept any `IPageTopicViewModel` as a generic argument; that will be used as the view model.
25
25
-**`FallbackController`**: Used in a [Controller Factory](#controller-factory) as a fallback, in case no other controllers can accept the request. Simply returns a `NotFoundResult` with a predefined message.
26
26
-**`LayoutControllerBase<T>`**: Provides support for a navigation menu by automatically mapping the top three tiers of the current namespace (e.g., `Web`, its children, and grandchildren). Can accept any `INavigationTopicViewModel` as a generic argument; that will be used as the view model for each mapped instance.
27
+
-**`CachedLayoutControllerBase<T>`**: Introduces specialized caching of `INavigationTopicViewModel<T>` graphs whenever a call to the `GetNavigationRoot()` is called. This avoids mapping the entirety of the navigation on each request.
27
28
-**`RedirectController`**: Provides a single `Redirect` action which can be bound to a route such as `/Topic/{ID}/`; this provides support for permanent URLs that are independent of the `GetWebPath()`.
28
29
-**`SitemapController`**: Provides a single `Sitemap` action which returns a reference to the `ITopicRepository`, thus allowing a sitemap view to recurse over the entire Topic graph, including all attributes.
Copy file name to clipboardExpand all lines: Ignia.Topics/Mapping/README.md
+9-3Lines changed: 9 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -156,7 +156,7 @@ This can be useful for filtering a collection. For instance, if a `CompanyTopicV
156
156
While it's not a best practice, this also works for strongly-typed collections of `Topic` objects. Typically, collections should return view models, but if the collection is strongly-typed to `Topic` (or a derivative) then the source `Topic` will not be mapped, and will be used as-is assuming it implements (or derives from) the target `Topic` type. This can be useful for scenarios where a view needs full access to the object graph (such as the `SitemapController`). In such cases, it is impractical to map the entirety of an object graph, along with all attributes, to a corresponding view model graph, and makes more sense to simply return the `Topic` graph.
157
157
158
158
## Caching
159
-
By default, the `TopicMappingService` will cache a reference to all types discovered that end with `TopicViewModel`, as well as all `MemberInfo` objects associated with each of those types. That mitigates much of the performance hit associated with the use of reflection. Despite that, simply setting properties—and, especially, on large object graphs—can require a lot of processing time. To mitigate this, OnTopic also offers two approaches.
159
+
By default, the `TopicMappingService` will cache a reference to all `MemberInfo` objects associated with each of view model it maps. That mitigates much of the performance hit associated with the use of reflection. Despite that, simply setting properties—and, especially, on large object graphs—can require a lot of processing time. To address this, OnTopic also offers two approaches.
160
160
161
161
### Internal Caching
162
162
When a request is made to `TopicMappingService`, and internal cache is constructed. If any mapping requests refer to a `Topic` that's already been mapped as part of the _current_ object graph, then that object will be returned. This prevents unnecessary duplication of mapping, and also avoids the potential for infinite loops. For instance, if a view model includes `Children`, and those children are set to `[Follow(Relationships.Parents)]`, the `TopicMappingService` will point back to the originally-mapped `Parent` object, instead of mapping a new instance of that `Topic`.
@@ -174,6 +174,12 @@ var topicMappingService = new TopicMappingService(topicRepository);
174
174
var cachedTopicMappingService = new CachedTopicMappingService(topicMappingService);
175
175
```
176
176
177
-
> *Note:* Be aware that the `CachedTopicMappingService` may take up considerable memory, depending on how many permutations of mapped objects the application has. This is especially true since it caches each unique object graph; no effort is made to centralize references to e.g. relationships that reference the same object instance.
177
+
> _**Important**_: Due to limitations discussed below, the application of the `CachedTopicMappingService` is quite restricted. It is likely inapprorpiate for page content, since that wouldn't reflect changes made via the editor. And it isn't appropriate for e.g. the `LayoutControllerBase{T}`, since it manually constructs its tree.
178
178
179
-
> *Note:* The `CachedTopicMappingService` makes no effort to validate or evict cache entries. Topics whose values change during the lifetime of the `CachedTopicMappingService` will not be reflected in the mapped responses.
179
+
180
+
#### Limitations
181
+
While the `CachedTopicMappingService` can be useful for particular scenarios, it introduces several limitations that should be accounted for.
182
+
183
+
1. It may take up considerable memory, depending on how many permutations of mapped objects the application has. This is especially true since it caches each unique object graph; no effort is made to centralize object instances referenced by e.g. relationships in multiple graphs.
184
+
2. It makes no effort to validate or evict cache entries. Topics whose values change during the lifetime of the `CachedTopicMappingService` will not be reflected in the mapped responses.
185
+
3. If a graph is manually constructed (by e.g. programmatically mapping `Children`) then each instance will be separated cached, thus potentially allowing an instance to be shared between multiple graphs. This can introduce concerns if edge maintenance is important.
0 commit comments