Skip to content

Commit 74d5907

Browse files
committed
Derived TopicReferenceCollection from the new TrackedCollection
The `TrackedCollection` offers a generic foundation for `TopicReferenceCollection` (995b6fe). As it centralizes much of the functionality, that allows us to remove much of this functionality from the `TopicReferenceCollection`. The only pieces that remain needed are a) the `IsFullyLoaded` property for tracking whether references were able to be merged into the local topic graph during load, and b) the protect overrides (i.e.., `InsertItem()`, `SetItem()`, `RemoveItem()`, and `ClearItems()`) so that we can handle the recipricol relationships (via `Topic.IncomingRelationships`) that's specific to the topic references, but not other tracked collections. There are a couple of breaking changes due to generalizing the nomenclature. Most notably, `GetTopic()` and `SetTopic()` have been renamed to `GetValue()` and `SetValue()`. As this refers specifically to the `TrackedItem<T>.Value` field, that isn't unreasonable—though it does remove the hint that this is setting or getting a `Topic` specifically. Finally, as this now derives from `KeyedCollection<>` instead of implementing `IDictionary<>`, a number of dictionary-specific methods have been removed, and the semantics are updated slightly. Most notably, the indexer no longer supports setting the value. As part of this, I also renamed it from `TopicReferenceDictionary` to `TopicReferenceCollection` to better articulate this change in semantics. As the `TopicReferenceCollection` is new to OnTopic 5.x, these changes aren't expected to have any impact on downstream implementations—outside of OnTopic Editor, which is working off a preview release of OnTopic 5.x—and thus shouldn't have any impact on customers. The next few commits will resolve the breaking changes within the OnTopic library itself.
1 parent 400786d commit 74d5907

4 files changed

Lines changed: 153 additions & 393 deletions

File tree

OnTopic.Tests/TopicReferenceDictionaryTest.cs renamed to OnTopic.Tests/TopicReferenceCollectionTest.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
using Microsoft.VisualStudio.TestTools.UnitTesting;
88
using OnTopic.References;
99
using OnTopic.Tests.Entities;
10+
using OnTopic.Collections.Specialized;
1011

1112
namespace OnTopic.Tests {
1213

1314
/*============================================================================================================================
14-
| CLASS: TOPIC REFERENCE DICTIONARY TEST
15+
| CLASS: TOPIC REFERENCE COLLECTION TEST
1516
\---------------------------------------------------------------------------------------------------------------------------*/
1617
/// <summary>
1718
/// Provides unit tests for the <see cref="TopicReferenceDictionary"/>, with a particular emphasis on the custom features
@@ -20,7 +21,7 @@ namespace OnTopic.Tests {
2021
/// values in the <see cref="Topic.IncomingRelationships"/> property.
2122
/// </summary>
2223
[TestClass]
23-
public class TopicReferenceDictionaryTest {
24+
public class TopicReferenceCollectionTest {
2425

2526
/*==========================================================================================================================
2627
| TEST: ADD: NEW REFERENCE: IS DIRTY
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
using System;
7+
using OnTopic.Collections.Specialized;
8+
using OnTopic.Repositories;
9+
10+
namespace OnTopic.References {
11+
12+
/*============================================================================================================================
13+
| CLASS: TOPIC REFERENCE COLLECTION
14+
\---------------------------------------------------------------------------------------------------------------------------*/
15+
/// <summary>
16+
/// Represents a collection of <see cref="Topic"/> objects associated with particular reference keys.
17+
/// </summary>
18+
public class TopicReferenceCollection : TrackedCollection<TopicReference, Topic, ReferenceSetterAttribute> {
19+
20+
/*==========================================================================================================================
21+
| CONSTRUCTOR
22+
\-------------------------------------------------------------------------------------------------------------------------*/
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="TopicReferenceCollection"/>.
25+
/// </summary>
26+
/// <param name="parentTopic">A reference to the topic that the current collection is bound to.</param>
27+
public TopicReferenceCollection(Topic parentTopic) : base(parentTopic) { }
28+
29+
/*==========================================================================================================================
30+
| PROPERTY: PARENT COLLECTION
31+
\-------------------------------------------------------------------------------------------------------------------------*/
32+
/// <inheritdoc/>
33+
protected override TrackedCollection<TopicReference, Topic, ReferenceSetterAttribute>? ParentCollection =>
34+
AssociatedTopic.Parent?.References;
35+
36+
/*==========================================================================================================================
37+
| PROPERTY: BASE COLLECTION
38+
\-------------------------------------------------------------------------------------------------------------------------*/
39+
/// <inheritdoc/>
40+
protected override TrackedCollection<TopicReference, Topic, ReferenceSetterAttribute>? BaseCollection =>
41+
AssociatedTopic.BaseTopic?.References;
42+
43+
/*==========================================================================================================================
44+
| IS FULLY LOADED?
45+
\-------------------------------------------------------------------------------------------------------------------------*/
46+
/// <summary>
47+
/// Determines whether or not the collection was fully loaded from the persistence store.
48+
/// </summary>
49+
/// <remarks>
50+
/// <para>
51+
/// When loading an individual <see cref="Topic"/> or branch from the persistence store, it is possible that topic
52+
/// references may not be fully available. In this scenario, updating topic references while e.g. deleting unmatched
53+
/// relationships can result in unintended data loss. To account for this, the <see cref="IsFullyLoaded"/> property '
54+
/// tracks whether a collection was fully loaded from the persistence store; if it wasn't, the <see cref="
55+
/// ITopicRepository"/> should not deleted unmatched topic references.
56+
/// </para>
57+
/// <para>
58+
/// The <see cref="IsFullyLoaded"/> property defaults to <c>true</c>. It should be set to <c>false</c> during the <see cref="
59+
/// ITopicRepository.Load(String?, Topic?, Boolean)"/> method if any members of the collection cannot be mapped back to
60+
/// a valid <see cref="Topic"/> reference in memory.
61+
/// </para>
62+
/// </remarks>
63+
public bool IsFullyLoaded { get; set; } = true;
64+
65+
/*==========================================================================================================================
66+
| INSERT ITEM
67+
\-------------------------------------------------------------------------------------------------------------------------*/
68+
/// <inheritdoc/>
69+
protected override void InsertItem(int index, TopicReference item) {
70+
71+
/*------------------------------------------------------------------------------------------------------------------------
72+
| Provide base logic
73+
\-----------------------------------------------------------------------------------------------------------------------*/
74+
base.InsertItem(index, item);
75+
76+
/*------------------------------------------------------------------------------------------------------------------------
77+
| Handle recipricol references
78+
\-----------------------------------------------------------------------------------------------------------------------*/
79+
item?.Value?.IncomingRelationships.SetTopic(item.Key, AssociatedTopic, null, true);
80+
81+
}
82+
83+
/*==========================================================================================================================
84+
| OVERRIDE: SET ITEM
85+
\-------------------------------------------------------------------------------------------------------------------------*/
86+
/// <inheritdoc/>
87+
protected override void SetItem(int index, TopicReference item) {
88+
89+
/*------------------------------------------------------------------------------------------------------------------------
90+
| Get existing reference
91+
\-----------------------------------------------------------------------------------------------------------------------*/
92+
var existingItem = this[index];
93+
94+
/*------------------------------------------------------------------------------------------------------------------------
95+
| Provide base logic
96+
\-----------------------------------------------------------------------------------------------------------------------*/
97+
base.SetItem(index, item);
98+
99+
/*------------------------------------------------------------------------------------------------------------------------
100+
| Handle recipricol references
101+
\-----------------------------------------------------------------------------------------------------------------------*/
102+
item?.Value?.IncomingRelationships.SetTopic(item.Key, AssociatedTopic, null, true);
103+
existingItem?.Value?.IncomingRelationships.SetTopic(existingItem.Key, AssociatedTopic, null, true);
104+
105+
}
106+
107+
/*==========================================================================================================================
108+
| OVERRIDE: REMOVE ITEM
109+
\-------------------------------------------------------------------------------------------------------------------------*/
110+
/// <inheritdoc/>
111+
protected override sealed void RemoveItem(int index) {
112+
113+
/*------------------------------------------------------------------------------------------------------------------------
114+
| Handle recipricol references
115+
\-----------------------------------------------------------------------------------------------------------------------*/
116+
var existing = this[index];
117+
118+
existing.Value?.IncomingRelationships.RemoveTopic(existing.Key, AssociatedTopic, true);
119+
120+
/*------------------------------------------------------------------------------------------------------------------------
121+
| Provide base logic
122+
\-----------------------------------------------------------------------------------------------------------------------*/
123+
base.RemoveItem(index);
124+
125+
}
126+
127+
/*==========================================================================================================================
128+
| OVERRIDE: CLEAR ITEMS
129+
\-------------------------------------------------------------------------------------------------------------------------*/
130+
/// <inheritdoc/>
131+
protected override sealed void ClearItems() {
132+
133+
134+
/*------------------------------------------------------------------------------------------------------------------------
135+
| Provide base logic
136+
\-----------------------------------------------------------------------------------------------------------------------*/
137+
base.ClearItems();
138+
139+
/*------------------------------------------------------------------------------------------------------------------------
140+
| Handle recipricol references
141+
\-----------------------------------------------------------------------------------------------------------------------*/
142+
foreach (var item in Items) {
143+
item.Value?.IncomingRelationships.RemoveTopic(item.Key, AssociatedTopic, true);
144+
}
145+
146+
}
147+
148+
} //Class
149+
} //Namespace

0 commit comments

Comments
 (0)