Skip to content

Commit a3b6da1

Browse files
committed
Don't persist unsaved Topic.DerivedTopic
Previously, when the `Topic.DerivedTopic` was set, the underlying `Topic.Attributes`'s `TopicID` attribute was also set—even if the `DerivedTopic.Id` was `-1` (i.e., meaning it was unsaved). This provides no value to the persistence store since that `Topic.Id` cannot be used to rehydrate the topic graph. This update prevents this from happening by putting in a check that the `DerivedTopic.Id` is a positive integer (i.e., has been saved). Of course, the source topic will need to be saved once the `DerivedTopic` is saved to ensure that the relationship is persisted. This brings this functionality in line with how relationships work. Includes unit tests for validating the behavior.
1 parent b4105f3 commit a3b6da1

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

OnTopic.Tests/TopicTest.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using OnTopic.Metadata;
1010
using Microsoft.VisualStudio.TestTools.UnitTesting;
1111
using OnTopic.Collections;
12+
using OnTopic.Attributes;
1213

1314
namespace OnTopic.Tests {
1415

@@ -278,21 +279,71 @@ public void LastModified_UpdateValue_ReturnsExpectedValue() {
278279
Assert.AreEqual<DateTime>(lastModified, topic.LastModified);
279280

280281
}
282+
281283
/*==========================================================================================================================
282284
| TEST: DERIVED TOPIC: UPDATE VALUE: RETURNS EXPECTED VALUE
283285
\-------------------------------------------------------------------------------------------------------------------------*/
284286
/// <summary>
285-
/// Sets a derived topic, and ensures it is referenced correctly.
287+
/// Sets a derived topic to a topic entity, then replaces the references with a new topic entity. Ensures that both the
288+
/// derived topic as well as the underlying <see cref="AttributeValue"/> correctly reference the new value.
286289
/// </summary>
287290
[TestMethod]
288291
public void DerivedTopic_UpdateValue_ReturnsExpectedValue() {
289292

293+
var topic = TopicFactory.Create("Topic", "Page");
294+
var firstDerivedTopic = TopicFactory.Create("DerivedTopic", "Page");
295+
var secondDerivedTopic = TopicFactory.Create("DerivedTopic", "Page", 1);
296+
var finalDerivedTopic = TopicFactory.Create("DerivedTopic", "Page", 2);
297+
298+
topic.DerivedTopic = firstDerivedTopic;
299+
topic.DerivedTopic = secondDerivedTopic;
300+
topic.DerivedTopic = finalDerivedTopic;
301+
302+
Assert.ReferenceEquals(topic.DerivedTopic, finalDerivedTopic);
303+
Assert.AreEqual<int>(2, topic.Attributes.GetInteger("TopicID", 0));
304+
305+
}
306+
307+
/*==========================================================================================================================
308+
| TEST: DERIVED TOPIC: UNSAVED VALUE: RETURNS EXPECTED VALUE
309+
\-------------------------------------------------------------------------------------------------------------------------*/
310+
/// <summary>
311+
/// Sets a derived topic to an unsaved topic entity. Ensures that the derived topic is correctly set, but that the <see
312+
/// cref="Topic.Id"/> is not persisted as an underlying <see cref="AttributeValue"/>.
313+
/// </summary>
314+
[TestMethod]
315+
public void DerivedTopic_UnsavedValue_ReturnsExpectedValue() {
316+
290317
var topic = TopicFactory.Create("Topic", "Page");
291318
var derivedTopic = TopicFactory.Create("DerivedTopic", "Page");
292319

293320
topic.DerivedTopic = derivedTopic;
294321

295322
Assert.ReferenceEquals(topic.DerivedTopic, derivedTopic);
323+
Assert.AreEqual<int>(-2, topic.Attributes.GetInteger("TopicID", -2));
324+
325+
}
326+
327+
/*==========================================================================================================================
328+
| TEST: DERIVED TOPIC: RESAVED VALUE: RETURNS EXPECTED VALUE
329+
\-------------------------------------------------------------------------------------------------------------------------*/
330+
/// <summary>
331+
/// Sets a derived topic to an unsaved topic entity, then saves the entity and reestablishes the relationship. Ensures
332+
/// that the derived topic is correctly set, including the <see cref="Topic.Id"/> as an underlying <see
333+
/// cref="AttributeValue"/>.
334+
/// </summary>
335+
[TestMethod]
336+
public void DerivedTopic_ResavedValue_ReturnsExpectedValue() {
337+
338+
var topic = TopicFactory.Create("Topic", "Page");
339+
var derivedTopic = TopicFactory.Create("DerivedTopic", "Page");
340+
341+
topic.DerivedTopic = derivedTopic;
342+
derivedTopic.Id = 5;
343+
topic.DerivedTopic = derivedTopic;
344+
345+
Assert.ReferenceEquals(topic.DerivedTopic, derivedTopic);
346+
Assert.AreEqual<int>(5, topic.Attributes.GetInteger("TopicID", -2));
296347

297348
}
298349

OnTopic/Topic.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,16 @@ public string GetWebPath() {
537537
/// Be aware that while multiple levels of derived topics can be configured, the <see
538538
/// cref="AttributeValueCollection.GetValue(String, Boolean)" /> method defaults to a maximum level of five "hops".
539539
/// </para>
540+
/// <para>
541+
/// The underlying value of the <see cref="DerivedTopic"/> is stored as the <c>TopicID</c> <see cref="AttributeValue"/>.
542+
/// If the <see cref="Topic"/> hasn't been saved, then the relationship will be established, but the <c>TopicID</c>
543+
/// won't be persisted to the underlying repository upon <see cref="Repositories.ITopicRepository.Save"/>. That said,
544+
/// when <see cref="Repositories.TopicRepositoryBase.Save"/> is called, the <see cref="DerivedTopic"/> will be
545+
/// reevaluated and, if it has subsequently been saved, then the <c>TopicID</c> will be updated accordingly. This allows
546+
/// in-memory topic graphs to be constructed, while preventing invalid <see cref="Topic.Id"/>s from being persisted to
547+
/// the underlying data storage. As a result, however, a <see cref="Topic"/> referencing a <see cref="DerivedTopic"/>
548+
/// that is unsaved will need to be saved again once the <see cref="DerivedTopic"/> has been saved.
549+
/// </para>
540550
/// </remarks>
541551
/// <value>The <see cref="Topic"/> that values should be derived from, if not otherwise available.</value>
542552
/// <requires description="A topic key must not derive from itself." exception="T:System.ArgumentException">
@@ -549,11 +559,8 @@ public Topic? DerivedTopic {
549559
value != this,
550560
"A topic may not derive from itself."
551561
);
552-
if (!_derivedTopic?.Equals(value)?? false) {
553-
return;
554-
}
555562
_derivedTopic = value;
556-
if (value != null) {
563+
if (value != null && value.Id > 0) {
557564
SetAttributeValue("TopicID", value.Id.ToString(CultureInfo.InvariantCulture));
558565
}
559566
else {

0 commit comments

Comments
 (0)