Skip to content

Commit c757dd3

Browse files
committed
Merge branch 'feature/DirtyKeyCollection' into develop
Introduced a new `DirtyKeyCollection` to standardize how dirty keys are tracked in e.g. `TopicRelationshipMultiMap` and `TopicReferenceDictionary`. As part of this, also introduced a new `ITrackDirtyKeys` interface to standardize the interface and centralize the documentation for `IsDirty()` and `MarkClean()` methods between `TopicRelationshipMultiMap`, `TopicReferenceDictionary`, and `AttributeValueCollection` (the latter which doesn't currently use `DirtyKeyCollection`). Finally, I moved `TopicMultiMap`, `ReadOnlyTopicMultiMap`, `KeyValuesPair`, and `TopicIndex` to a new `OnTopic.Collections.Specialized` collection since they're not typically utilized instantiated directly by customers, even if they may end up using some of them via return types. That keeps the `OnTopic.Collections` namespace focused on more common, customer-centric collections. (As these are all new types as of OnTopic 5.0.0, this isn't a breaking change.)
2 parents 7629582 + 816a9f0 commit c757dd3

13 files changed

Lines changed: 211 additions & 90 deletions

OnTopic.Data.Sql/SqlDataReaderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Net;
1212
using Microsoft.Data.SqlClient;
1313
using OnTopic.Collections;
14+
using OnTopic.Collections.Specialized;
1415
using OnTopic.Internal.Diagnostics;
1516
using OnTopic.Querying;
1617

OnTopic.Tests/TopicReferenceDictionaryTest.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace OnTopic.Tests {
1515
\---------------------------------------------------------------------------------------------------------------------------*/
1616
/// <summary>
1717
/// Provides unit tests for the <see cref="TopicReferenceDictionary"/>, with a particular emphasis on the custom features
18-
/// such as <see cref="TopicReferenceDictionary.IsDirty"/>, <see cref="TopicReferenceDictionary.GetTopic(String, Boolean)"
18+
/// such as <see cref="TopicReferenceDictionary.IsDirty()"/>, <see cref="TopicReferenceDictionary.GetTopic(String, Boolean)"
1919
/// />, <see cref="TopicReferenceDictionary.SetTopic(String, Topic?, Boolean?)"/>, and the cross-referencing of reciprocal
2020
/// values in the <see cref="Topic.IncomingRelationships"/> property.
2121
/// </summary>
@@ -27,7 +27,7 @@ public class TopicReferenceDictionaryTest {
2727
\-------------------------------------------------------------------------------------------------------------------------*/
2828
/// <summary>
2929
/// Assembles a new <see cref="TopicReferenceDictionary"/>, adds a new <see cref="Topic"/> reference, and confirms that
30-
/// <see cref="TopicReferenceDictionary.IsDirty"/> is correctly set.
30+
/// <see cref="TopicReferenceDictionary.IsDirty()"/> is correctly set.
3131
/// </summary>
3232
[TestMethod]
3333
public void Add_NewReference_IsDirty() {
@@ -48,7 +48,7 @@ public void Add_NewReference_IsDirty() {
4848
/// <summary>
4949
/// Assembles a new <see cref="TopicReferenceDictionary"/>, adds a new <see cref="Topic"/> reference using <see
5050
/// cref="TopicReferenceDictionary.SetTopic(String, Topic?, Boolean?)"/>, and confirms that <see cref="
51-
/// TopicReferenceDictionary.IsDirty"/> is not set.
51+
/// TopicReferenceDictionary.IsDirty()"/> is not set.
5252
/// </summary>
5353
[TestMethod]
5454
public void SetTopic_NewReference_NotDirty() {
@@ -68,12 +68,13 @@ public void SetTopic_NewReference_NotDirty() {
6868
\-------------------------------------------------------------------------------------------------------------------------*/
6969
/// <summary>
7070
/// Assembles a new <see cref="TopicReferenceDictionary"/> with a topic reference, removes that reference using <see cref=
71-
/// "TopicReferenceDictionary.Remove(String)"/> , and confirms that <see cref="TopicReferenceDictionary.IsDirty"/> is set.
71+
/// "TopicReferenceDictionary.Remove(String)"/> , and confirms that <see cref="TopicReferenceDictionary.IsDirty()"/> is
72+
/// set.
7273
/// </summary>
7374
[TestMethod]
7475
public void Remove_ExistingReference_IsDirty() {
7576

76-
var topic = TopicFactory.Create("Topic", "Page");
77+
var topic = TopicFactory.Create("Topic", "Page", 1);
7778
var reference = TopicFactory.Create("Reference", "Page");
7879

7980
topic.References.SetTopic("Reference", reference, false);
@@ -90,12 +91,12 @@ public void Remove_ExistingReference_IsDirty() {
9091
/// <summary>
9192
/// Assembles a new <see cref="TopicReferenceDictionary"/>, adds a new <see cref="Topic"/> reference using <see
9293
/// cref="TopicReferenceDictionary.SetTopic(String, Topic?, Boolean?)"/>, calls <see cref="TopicReferenceDictionary
93-
/// .Clear()"/> and confirms that <see cref="TopicReferenceDictionary.IsDirty"/> is set.
94+
/// .Clear()"/> and confirms that <see cref="TopicReferenceDictionary.IsDirty()"/> is set.
9495
/// </summary>
9596
[TestMethod]
9697
public void Clear_ExistingReferences_IsDirty() {
9798

98-
var topic = TopicFactory.Create("Topic", "Page");
99+
var topic = TopicFactory.Create("Topic", "Page", 1);
99100
var reference = TopicFactory.Create("Reference", "Page");
100101

101102
topic.References.SetTopic("Reference", reference, false);

OnTopic.Tests/TopicRelationshipMultiMapTest.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using System;
77
using System.Linq;
88
using Microsoft.VisualStudio.TestTools.UnitTesting;
9-
using OnTopic.Collections;
9+
using OnTopic.Collections.Specialized;
1010
using OnTopic.References;
1111

1212
namespace OnTopic.Tests {
@@ -166,8 +166,8 @@ public void GetAllTopics_ContentTypes_ReturnsAllContentTypes() {
166166
| TEST: SET TOPIC: IS DIRTY
167167
\-------------------------------------------------------------------------------------------------------------------------*/
168168
/// <summary>
169-
/// Adds a topic to a <see cref="TopicRelationshipMultiMap"/> and confirms that <see cref="TopicRelationshipMultiMap.IsDirty"/> is
170-
/// set.
169+
/// Adds a topic to a <see cref="TopicRelationshipMultiMap"/> and confirms that <see cref="TopicRelationshipMultiMap.
170+
/// IsDirty()"/> is set.
171171
/// </summary>
172172
[TestMethod]
173173
public void SetTopic_IsDirty() {
@@ -187,7 +187,7 @@ public void SetTopic_IsDirty() {
187187
\-------------------------------------------------------------------------------------------------------------------------*/
188188
/// <summary>
189189
/// Adds a duplicate topic to a <see cref="TopicRelationshipMultiMap"/> and confirms that value of <see
190-
/// cref="TopicRelationshipMultiMap.IsDirty"/> is <c>false</c>.
190+
/// cref="TopicRelationshipMultiMap.IsDirty()"/> is <c>false</c>.
191191
/// </summary>
192192
[TestMethod]
193193
public void SetTopic_IsDuplicate_IsNotDirty() {
@@ -210,7 +210,7 @@ public void SetTopic_IsDuplicate_IsNotDirty() {
210210
\-------------------------------------------------------------------------------------------------------------------------*/
211211
/// <summary>
212212
/// Adds a duplicate topic to a <see cref="TopicRelationshipMultiMap"/> and confirms that value of <see
213-
/// cref="TopicRelationshipMultiMap.IsDirty"/> is <c>false</c>.
213+
/// cref="TopicRelationshipMultiMap.IsDirty()"/> is <c>false</c>.
214214
/// </summary>
215215
[TestMethod]
216216
public void SetTopic_IsDuplicate_StaysDirty() {
@@ -233,12 +233,12 @@ public void SetTopic_IsDuplicate_StaysDirty() {
233233
\-------------------------------------------------------------------------------------------------------------------------*/
234234
/// <summary>
235235
/// Removes an existing <see cref="Topic"/> from a <see cref="TopicRelationshipMultiMap"/> and conirms that the value for <see
236-
/// cref="TopicRelationshipMultiMap.IsDirty"/> returns <c>true</c>.
236+
/// cref="TopicRelationshipMultiMap.IsDirty()"/> returns <c>true</c>.
237237
/// </summary>
238238
[TestMethod]
239239
public void RemoveTopic_IsDirty() {
240240

241-
var topic = TopicFactory.Create("Test", "Page");
241+
var topic = TopicFactory.Create("Test", "Page", 1);
242242
var relationships = new TopicRelationshipMultiMap(topic);
243243
var related = TopicFactory.Create("Topic", "Page");
244244

@@ -255,7 +255,7 @@ public void RemoveTopic_IsDirty() {
255255
\-------------------------------------------------------------------------------------------------------------------------*/
256256
/// <summary>
257257
/// Removes a non-existent <see cref="Topic"/> from a <see cref="TopicRelationshipMultiMap"/> and conirms that the value for
258-
/// <see cref="TopicRelationshipMultiMap.IsDirty"/> returns <c>false</c>.
258+
/// <see cref="TopicRelationshipMultiMap.IsDirty()"/> returns <c>false</c>.
259259
/// </summary>
260260
[TestMethod]
261261
public void RemoveTopic_MissingTopic_IsNotDirty() {
@@ -276,7 +276,7 @@ public void RemoveTopic_MissingTopic_IsNotDirty() {
276276
\-------------------------------------------------------------------------------------------------------------------------*/
277277
/// <summary>
278278
/// Removes a non-existent <see cref="Topic"/> from a <see cref="TopicRelationshipMultiMap"/> and conirms that the value for
279-
/// <see cref="TopicRelationshipMultiMap.IsDirty"/> stays <c>true</c>.
279+
/// <see cref="TopicRelationshipMultiMap.IsDirty()"/> stays <c>true</c>.
280280
/// </summary>
281281
[TestMethod]
282282
public void RemoveTopic_MissingTopic_StaysDirty() {
@@ -300,12 +300,12 @@ public void RemoveTopic_MissingTopic_StaysDirty() {
300300
\-------------------------------------------------------------------------------------------------------------------------*/
301301
/// <summary>
302302
/// Call <see cref="TopicRelationshipMultiMap.ClearTopics(String)"/> and confirms that value of <see
303-
/// cref="TopicRelationshipMultiMap.IsDirty"/> is <c>true</c>.
303+
/// cref="TopicRelationshipMultiMap.IsDirty()"/> is <c>true</c>.
304304
/// </summary>
305305
[TestMethod]
306306
public void ClearTopics_ExistingTopics_IsDirty() {
307307

308-
var topic = TopicFactory.Create("Test", "Page");
308+
var topic = TopicFactory.Create("Test", "Page", 1);
309309
var relationships = new TopicRelationshipMultiMap(topic);
310310
var related = TopicFactory.Create("Topic", "Page");
311311

@@ -322,7 +322,7 @@ public void ClearTopics_ExistingTopics_IsDirty() {
322322
\-------------------------------------------------------------------------------------------------------------------------*/
323323
/// <summary>
324324
/// Call <see cref="TopicRelationshipMultiMap.ClearTopics(String)"/> with no existing <see cref="Topic"/>s and confirms that
325-
/// the value of <see cref="TopicRelationshipMultiMap.IsDirty"/> is set to <c>false</c>.
325+
/// the value of <see cref="TopicRelationshipMultiMap.IsDirty()"/> is set to <c>false</c>.
326326
/// </summary>
327327
[TestMethod]
328328
public void ClearTopics_NoTopics_IsNotDirty() {

OnTopic/Attributes/AttributeValueCollection.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.ObjectModel;
99
using System.Diagnostics.CodeAnalysis;
1010
using System.Linq;
11+
using OnTopic.Collections.Specialized;
1112
using OnTopic.Internal.Diagnostics;
1213
using OnTopic.Internal.Reflection;
1314
using OnTopic.Repositories;
@@ -25,7 +26,7 @@ namespace OnTopic.Attributes {
2526
/// The <see cref="Topic"/> class tracks these through its <see cref="Topic.Attributes"/> property, which is an instance of
2627
/// the <see cref="AttributeValueCollection"/> class.
2728
/// </remarks>
28-
public class AttributeValueCollection : KeyedCollection<string, AttributeValue> {
29+
public class AttributeValueCollection : KeyedCollection<string, AttributeValue>, ITrackDirtyKeys {
2930

3031
/*==========================================================================================================================
3132
| DISPATCHER
@@ -73,6 +74,10 @@ internal AttributeValueCollection(Topic parentTopic) : base(StringComparer.Ordin
7374
/*==========================================================================================================================
7475
| METHOD: IS DIRTY
7576
\-------------------------------------------------------------------------------------------------------------------------*/
77+
78+
/// <inheritdoc/>
79+
public bool IsDirty() => IsDirty(false);
80+
7681
/// <summary>
7782
/// Determine if <i>any</i> attributes in the <see cref="AttributeValueCollection"/> are dirty.
7883
/// </summary>
@@ -88,7 +93,7 @@ internal AttributeValueCollection(Topic parentTopic) : base(StringComparer.Ordin
8893
/// generated by e.g. the OnTopic Editor and, thus, may be irrelevant updates if no other attribute values have changed.
8994
/// </param>
9095
/// <returns>True if the attribute value is marked as dirty; otherwise false.</returns>
91-
public bool IsDirty(bool excludeLastModified = false)
96+
public bool IsDirty(bool excludeLastModified)
9297
=> DeletedAttributes.Count > 0 || Items.Any(a =>
9398
a.IsDirty &&
9499
(!excludeLastModified || !a.Key.StartsWith("LastModified", StringComparison.OrdinalIgnoreCase))
@@ -103,18 +108,22 @@ public bool IsDirty(bool excludeLastModified = false)
103108
/// </c> is a state of the current <see cref="AttributeValue"/>, it does not support <c>inheritFromParent</c> or <c>
104109
/// inheritFromDerived</c> (which otherwise default to <c>true</c>).
105110
/// </remarks>
106-
/// <param name="name">The string identifier for the <see cref="AttributeValue"/>.</param>
111+
/// <param name="key">The string identifier for the <see cref="AttributeValue"/>.</param>
107112
/// <returns>True if the attribute value is marked as dirty; otherwise false.</returns>
108-
public bool IsDirty(string name) {
109-
if (!Contains(name)) {
113+
public bool IsDirty(string key) {
114+
if (!Contains(key)) {
110115
return false;
111116
}
112-
return this[name].IsDirty;
117+
return this[key].IsDirty;
113118
}
114119

115120
/*==========================================================================================================================
116121
| METHOD: MARK CLEAN
117122
\-------------------------------------------------------------------------------------------------------------------------*/
123+
124+
/// <inheritdoc/>
125+
public void MarkClean() => MarkClean((DateTime?)null);
126+
118127
/// <summary>
119128
/// Marks the collection—including all <see cref="AttributeValue"/> items—as clean, meaning they have been persisted to
120129
/// the underlying <see cref="ITopicRepository"/>.
@@ -128,7 +137,7 @@ public bool IsDirty(string name) {
128137
/// The <see cref="DateTime"/> value that the attributes were last saved. This corresponds to the <see cref="Topic.
129138
/// VersionHistory"/>.
130139
/// </param>
131-
public void MarkClean(DateTime? version = null) {
140+
public void MarkClean(DateTime? version) {
132141
foreach (var attribute in Items.Where(a => a.IsDirty).ToArray()) {
133142
SetValue(attribute.Key, attribute.Value, false, false, version?? DateTime.UtcNow);
134143
}
@@ -138,6 +147,10 @@ public void MarkClean(DateTime? version = null) {
138147
/*==========================================================================================================================
139148
| METHOD: MARK CLEAN
140149
\-------------------------------------------------------------------------------------------------------------------------*/
150+
151+
/// <inheritdoc/>
152+
public void MarkClean(string key) => MarkClean(key, null);
153+
141154
/// <summary>
142155
/// Marks an individual <see cref="AttributeValue"/> as clean.
143156
/// </summary>
@@ -146,14 +159,14 @@ public void MarkClean(DateTime? version = null) {
146159
/// mark an <see cref="AttributeValue"/> as clean. After this, <see cref="IsDirty(String)"/> will return <c>false</c> for
147160
/// that item until it is modified.
148161
/// </remarks>
149-
/// <param name="name">The string identifier for the <see cref="AttributeValue"/>.</param>
162+
/// <param name="key">The string identifier for the <see cref="AttributeValue"/>.</param>
150163
/// <param name="version">
151164
/// The <see cref="DateTime"/> value that the attribute was last modified. This denotes the <see cref="Topic.
152165
/// VersionHistory"/> associated with the specific attribute.
153166
/// </param>
154-
public void MarkClean(string name, DateTime? version = null) {
155-
if (Contains(name)) {
156-
var attribute = this[name];
167+
public void MarkClean(string key, DateTime? version) {
168+
if (Contains(key)) {
169+
var attribute = this[key];
157170
if (attribute.IsDirty) {
158171
SetValue(attribute.Key, attribute.Value, false, false, version?? DateTime.UtcNow);
159172
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*==============================================================================================================================
2+
| Author Ignia, LLC
3+
| Client Ignia, LLC
4+
| Project Topics Library
5+
\=============================================================================================================================*/
6+
using System.Collections.ObjectModel;
7+
8+
namespace OnTopic.Collections.Specialized {
9+
10+
/*============================================================================================================================
11+
| CLASS: DIRTY KEY COLLECTION
12+
\---------------------------------------------------------------------------------------------------------------------------*/
13+
/// <summary>
14+
/// Represents a collection of dirty keys.
15+
/// </summary>
16+
/// <remarks>
17+
/// This collection does not track the values of those keys or attempt to determine if a value is dirty. It simply provides
18+
/// a convenient way for other collections to track dirty keys based on their own internal logic.
19+
/// </remarks>
20+
internal class DirtyKeyCollection : Collection<string>, ITrackDirtyKeys {
21+
22+
/*==========================================================================================================================
23+
| CONSTRUCTOR
24+
\-------------------------------------------------------------------------------------------------------------------------*/
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="TopicCollection"/>.
27+
/// </summary>
28+
public DirtyKeyCollection() : base() {}
29+
30+
/*==========================================================================================================================
31+
| METHOD: IS DIRTY?
32+
\-------------------------------------------------------------------------------------------------------------------------*/
33+
/// <inheritdoc/>
34+
public bool IsDirty() => Count > 0;
35+
36+
/// <inheritdoc/>
37+
public bool IsDirty(string key) => Contains(key);
38+
39+
/*==========================================================================================================================
40+
| METHOD: MARK CLEAN
41+
\-------------------------------------------------------------------------------------------------------------------------*/
42+
/// <inheritdoc/>
43+
public void MarkClean() => Clear();
44+
45+
/// <inheritdoc/>
46+
public void MarkClean(string key) {
47+
if (Contains(key)) {
48+
Remove(key);
49+
}
50+
}
51+
52+
/*==========================================================================================================================
53+
| METHOD: MARK DIRTY
54+
\-------------------------------------------------------------------------------------------------------------------------*/
55+
/// <summary>
56+
/// Marks a specific <paramref name="key"/> as dirty, if it isn't already.
57+
/// </summary>
58+
public void MarkDirty(string key) {
59+
if (!Contains(key)) {
60+
Add(key);
61+
}
62+
}
63+
64+
/*==========================================================================================================================
65+
| METHOD: MARK AS
66+
\-------------------------------------------------------------------------------------------------------------------------*/
67+
/// <summary>
68+
/// Marks a specific <paramref name="key"/> as clean or dirty based on the <paramref name="markDirty"/> parameter.
69+
/// </summary>
70+
public void MarkAs(string key, bool markDirty) {
71+
if (markDirty) {
72+
MarkDirty(key);
73+
}
74+
else {
75+
MarkClean(key);
76+
}
77+
}
78+
79+
} //Class
80+
} //Namespace

0 commit comments

Comments
 (0)