Skip to content

Commit 7a5a243

Browse files
committed
Introduced new GetContentTypeDescriptors() overload
Previously, the `GetContentTypeDescriptors()` method _exclusively_ relied on retrieving the content type descriptors from the underlying data storage of the concrete implementation. This makes good sense in most read-optimized scenarios, where the metadata schema is well-established. This introduces problems, however, when trying to persist large, in-memory topic graphs to the storage medium. In those cases, we may need to dynamically update the `GetContentTypeDescriptors()` cache with as-of-yet-unsaved `ContentTypeDescriptor`s in order to bootstrap the database. For example, imagine trying to save the `Root` topic (a `Container` content type) before the `Container` content type descriptor has been persisted to the database. While this update doesn't support that functionality, it lays the groundwork by allowing implementers to pass an in-memory `ContentTypeDescriptor` to the `GetContentTypeDescriptors()` and either a) use that _instead_ of the data source (potentially useful for bootstrapping the database), or b) merge new `ContentTypeDesciptor`s with the existing cached data (potentially useful when importing large changes, as needed for the **OnTopic Data Exchange** library). The logic for this largely repeats the existing logic for `GetContentTypeDescriptors()`, so its been modified to load the topic graph from the underlying data source, and then pass it to this new overload as to keep that logic centralized. In this way, this overload maintains existing functionality, while providing additional flexibility for implementers. This will also lay the groundwork for the newly proposed features: - Update content type cache when adding or removing content types (#16), - Update attribute descriptors when adding or removing an attribute (#17), and - Discover in-memory `ContentTypeDescriptor`, `AttributeDescriptor` on `Save()` (#18). Includes a unit test to validate the ability to merge new in-memory `ContentTypeDescriptor`s into the `GetContentTypeDescriptors()` cache.
1 parent 6f825f5 commit 7a5a243

3 files changed

Lines changed: 88 additions & 17 deletions

File tree

OnTopic.TestDoubles/StubTopicRepository.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ public IEnumerable<AttributeValue> GetAttributesProxy(Topic topic, bool? isExten
177177
/// <inheritdoc cref="TopicRepositoryBase.GetUnmatchedAttributes(Topic)" />
178178
public IEnumerable<AttributeDescriptor> GetUnmatchedAttributesProxy(Topic topic) => base.GetUnmatchedAttributes(topic);
179179

180+
/*==========================================================================================================================
181+
| METHOD: GET CONTENT TYPE DESCRIPTORS (PROXY)
182+
\-------------------------------------------------------------------------------------------------------------------------*/
183+
/// <inheritdoc cref="TopicRepositoryBase.GetContentTypeDescriptors(ContentTypeDescriptor)" />
184+
public ContentTypeDescriptorCollection GetContentTypeDescriptorsProxy(ContentTypeDescriptor topicGraph) =>
185+
base.GetContentTypeDescriptors(topicGraph);
186+
180187
/*==========================================================================================================================
181188
| METHOD: CREATE FAKE DATA
182189
\-------------------------------------------------------------------------------------------------------------------------*/

OnTopic.Tests/TopicRepositoryBaseTest.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,27 @@ public void GetContentTypeDescriptors_ReturnsContentTypes() {
146146

147147
}
148148

149+
/*==========================================================================================================================
150+
| TEST: GET CONTENT TYPE DESCRIPTORS: WITH TOPIC GRAPH: RETURNS MERGED CONTENT TYPES
151+
\-------------------------------------------------------------------------------------------------------------------------*/
152+
/// <summary>
153+
/// Retrieves a list of <see cref="ContentTypeDescriptor"/>s from the <see cref="ITopicRepository"/> alongside a separate
154+
/// topic graph and ensures the two are properly merged.
155+
/// </summary>
156+
[TestMethod]
157+
public void GetContentTypeDescriptors_WithTopicGraph_ReturnsMergedContentTypes() {
158+
159+
var contentTypes = _topicRepository.GetContentTypeDescriptors();
160+
var rootContentType = contentTypes["ContentTypes"];
161+
var newContentType = TopicFactory.Create("NewContentType", "ContentTypeDescriptor", rootContentType);
162+
var contentTypeCount = contentTypes.Count;
163+
164+
_topicRepository.GetContentTypeDescriptorsProxy((ContentTypeDescriptor)newContentType);
165+
166+
Assert.AreNotEqual<int>(contentTypeCount, contentTypes.Count);
167+
Assert.IsNotNull(contentTypes.Contains(newContentType));
168+
169+
}
170+
149171
} //Class
150172
} //Namespace

OnTopic/Repositories/TopicRepositoryBase.cs

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using System.Collections.Generic;
1515
using System.Linq;
1616
using OnTopic.Collections;
17+
using OnTopic.Metadata.AttributeTypes;
1718

1819
#pragma warning disable CS0618 // Type or member is obsolete; used to hide known deprecation of events until v5.0.0
1920

@@ -66,33 +67,74 @@ public virtual ContentTypeDescriptorCollection GetContentTypeDescriptors() {
6667
Contract.Assume(configuration, $"The 'Root:Configuration' section could not be loaded from the 'ITopicRepository'.");
6768

6869
/*----------------------------------------------------------------------------------------------------------------------
69-
| Add available Content Types to the collection
70-
\---------------------------------------------------------------------------------------------------------------------*/
71-
_contentTypeDescriptors = new ContentTypeDescriptorCollection();
72-
73-
/*----------------------------------------------------------------------------------------------------------------------
74-
| Ensure the parent ContentTypes topic is available to iterate over
70+
| Load root content type
7571
\---------------------------------------------------------------------------------------------------------------------*/
76-
var allowedContentTypes = configuration.Children.GetTopic("ContentTypes");
72+
var allowedContentTypes = configuration.Children.GetTopic("ContentTypes") as ContentTypeDescriptor;
7773

7874
Contract.Assume(allowedContentTypes, "Unable to load section 'Configuration:ContentTypes'.");
7975

8076
/*----------------------------------------------------------------------------------------------------------------------
8177
| Add available Content Types to the collection
8278
\---------------------------------------------------------------------------------------------------------------------*/
83-
foreach (var topic in allowedContentTypes.FindAllByAttribute("ContentType", "ContentType")) {
84-
// Ensure the Topic is used as the strongly-typed ContentType
85-
// Add ContentType Topic to collection if not already added
86-
if (
87-
topic is ContentTypeDescriptor contentTypeDescriptor &&
88-
!_contentTypeDescriptors.Contains(contentTypeDescriptor.Key)
89-
) {
90-
_contentTypeDescriptors.Add(contentTypeDescriptor);
91-
}
92-
}
79+
_contentTypeDescriptors = GetContentTypeDescriptors(allowedContentTypes);
80+
81+
}
82+
83+
return _contentTypeDescriptors;
84+
85+
}
86+
87+
/// <summary>
88+
/// Optional overload of <see cref="GetContentTypeDescriptors()"/> allows for a new topic graph to be supplied for
89+
/// updating the list of cached <see cref="ContentTypeDescriptor"/>s.
90+
/// </summary>
91+
/// <remarks>
92+
/// By default, the <see cref="GetContentTypeDescriptors()"/> method will load data from the concrete implementation of
93+
/// the <see cref="ITopicRepository"/>'s data store. There are cases, however, where it may be preferrable to instead load
94+
/// these topics from a local, in-memory source. Namely, when first instantiating a new OnTopic database, and when saving
95+
/// modifications to existing content types. As such, this <c>protected</c> overload is useful to call from <see
96+
/// cref="ITopicRepository.Save(Topic, Boolean, Boolean)"/> when the topic graph being saved includes any <see
97+
/// cref="ContentTypeDescriptor"/>s.
98+
/// </remarks>
99+
/// <param name="contentTypeDescriptors">
100+
/// The root of a <see cref="ContentTypeDescriptor"/> topic graph to merge into the collection for <see
101+
/// cref="GetContentTypeDescriptors()"/>. The code will process not only the root <see cref="ContentTypeDescriptor"/>, but
102+
/// also any descendents.
103+
/// </param>
104+
/// <returns></returns>
105+
protected virtual ContentTypeDescriptorCollection GetContentTypeDescriptors(ContentTypeDescriptor contentTypeDescriptors) {
106+
107+
/*------------------------------------------------------------------------------------------------------------------------
108+
| Initialize the collection
109+
\-----------------------------------------------------------------------------------------------------------------------*/
110+
if (_contentTypeDescriptors == null) {
111+
_contentTypeDescriptors = new ContentTypeDescriptorCollection();
112+
}
93113

114+
/*------------------------------------------------------------------------------------------------------------------------
115+
| Validate parameters
116+
\-----------------------------------------------------------------------------------------------------------------------*/
117+
if (contentTypeDescriptors == null) {
118+
return _contentTypeDescriptors;
94119
}
95120

121+
/*------------------------------------------------------------------------------------------------------------------------
122+
| Add available Content Types to the collection
123+
\-----------------------------------------------------------------------------------------------------------------------*/
124+
foreach (var topic in contentTypeDescriptors.FindAllByAttribute("ContentType", "ContentType")) {
125+
// Ensure the Topic is used as the strongly-typed ContentType
126+
// Add ContentType Topic to collection if not already added
127+
if (
128+
topic is ContentTypeDescriptor contentTypeDescriptor &&
129+
!_contentTypeDescriptors.Contains(contentTypeDescriptor.Key)
130+
) {
131+
_contentTypeDescriptors.Add(contentTypeDescriptor);
132+
}
133+
}
134+
135+
/*------------------------------------------------------------------------------------------------------------------------
136+
| Add available Content Types to the collection
137+
\-----------------------------------------------------------------------------------------------------------------------*/
96138
return _contentTypeDescriptors;
97139

98140
}

0 commit comments

Comments
 (0)