diff --git a/src/Nitride/Entities/EntitySequence.cs b/src/Nitride/Entities/EntitySequence.cs
new file mode 100644
index 0000000..e90ac75
--- /dev/null
+++ b/src/Nitride/Entities/EntitySequence.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+
+using Gallium;
+
+namespace Nitride.Entities;
+
+///
+/// Defines a relationship between a given entity and the others in a sequence.
+///
+public class EntitySequence
+{
+ public EntitySequence(IReadOnlyList sequence, int index)
+ {
+ this.Sequence = sequence;
+ this.Index = index;
+ }
+
+ public bool HasNext => !this.IsLast;
+
+ public bool HasPrevious => !this.IsFirst;
+
+ public int Index { get; }
+
+ public bool IsFirst => this.Index == 0;
+
+ public bool IsLast => this.Index == this.Sequence.Count - 1;
+
+ public Entity? Next => this.HasNext ? this.Sequence[this.Index + 1] : null;
+
+ public Entity? Previous => this.HasPrevious ? this.Sequence[this.Index - 1] : null;
+
+ public IReadOnlyList Sequence { get; }
+}
diff --git a/src/Nitride/Entities/LinkEntitySequence.cs b/src/Nitride/Entities/LinkEntitySequence.cs
new file mode 100644
index 0000000..e7ba83b
--- /dev/null
+++ b/src/Nitride/Entities/LinkEntitySequence.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using FluentValidation;
+
+using Gallium;
+
+namespace Nitride.Entities;
+
+///
+/// Links a series of entities together in some manner. This assumes that
+/// the entities coming into the operation are already ordered.
+///
+[WithProperties]
+public partial class LinkEntitySequence : OperationBase, IResolvingOperation
+{
+ private readonly IValidator validator;
+
+ public LinkEntitySequence(IValidator validator)
+ {
+ this.validator = validator;
+ this.CreateSequenceIndex = (list, index) => new EntitySequence(list, index);
+ this.AddSequenceIndex = (entity, sequence) => entity.Add(sequence);
+ }
+
+ ///
+ /// Gets or sets a callback to add a sequence into a given entity.
+ ///
+ public Func AddSequenceIndex { get; set; }
+
+ ///
+ /// Gets or sets the function used to create the sequence index.
+ ///
+ public Func, int, EntitySequence> CreateSequenceIndex { get; set; }
+
+ ///
+ public override IEnumerable Run(IEnumerable input)
+ {
+ // Make sure everything is good.
+ this.validator.ValidateAndThrow(this);
+
+ // We pull all the entities into a list so we can walk through them
+ // more than once along with index access. We also don't reorder the
+ // list since we assumed it has been ordered already.
+ var list = input.ToList();
+
+ // Go through the list and assign each entity in order with a unique
+ // copy of the sequence.
+ for (int i = 0; i < list.Count; i++)
+ {
+ Entity entity = list[i];
+ EntitySequence index = this.CreateSequenceIndex(list.AsReadOnly(), i);
+
+ yield return this.AddSequenceIndex(entity, index);
+ }
+ }
+}
diff --git a/src/Nitride/Entities/LinkEntitySequenceValidator.cs b/src/Nitride/Entities/LinkEntitySequenceValidator.cs
new file mode 100644
index 0000000..92fb565
--- /dev/null
+++ b/src/Nitride/Entities/LinkEntitySequenceValidator.cs
@@ -0,0 +1,12 @@
+using FluentValidation;
+
+namespace Nitride.Entities;
+
+public class LinkEntitySequenceValidator : AbstractValidator
+{
+ public LinkEntitySequenceValidator()
+ {
+ this.RuleFor(x => x.CreateSequenceIndex).NotNull();
+ this.RuleFor(x => x.AddSequenceIndex).NotNull();
+ }
+}
diff --git a/src/Nitride/Entities/LinkPreviousNextHandler.cs b/src/Nitride/Entities/LinkPreviousNextHandler.cs
deleted file mode 100644
index 553d3c8..0000000
--- a/src/Nitride/Entities/LinkPreviousNextHandler.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using Gallium;
-
-namespace Nitride.Entities;
-
-public delegate Entity LinkPreviousNextHandler(Entity entity, Entity? previous, Entity? next);
diff --git a/src/Nitride/Entities/LinkSerialEntities.cs b/src/Nitride/Entities/LinkSerialEntities.cs
deleted file mode 100644
index 53d584c..0000000
--- a/src/Nitride/Entities/LinkSerialEntities.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-
-using FluentValidation;
-
-using Gallium;
-
-namespace Nitride.Entities;
-
-///
-/// Links a series of entities together in some manner. This assumes that
-/// the entities coming into the operation are already ordered.
-///
-[WithProperties]
-public partial class LinkSerialEntities : OperationBase
-{
- private readonly IValidator validator;
-
- public LinkSerialEntities(IValidator validator)
- {
- this.validator = validator;
- }
-
- ///
- /// The callback function that sets the previous and next
- ///
- public LinkPreviousNextHandler? UpdatePreviousNext { get; set; }
-
- ///
- public override IEnumerable Run(IEnumerable input)
- {
- // Make sure everything is good.
- this.validator.ValidateAndThrow(this);
-
- // We pull all the entities into a list so we can walk through them
- // more than once along with index access.
- var list = input.ToList();
-
- // Go through the list and assign each entity in order.
- for (int i = 0; i < list.Count; i++)
- {
- Entity entity = list[i];
- Entity? previous = i > 0 ? list[i - 1] : null;
- Entity? next = i < list.Count - 1 ? list[i + 1] : null;
-
- list[i] = this.UpdatePreviousNext!(entity, previous, next);
- }
-
- // Return the resulting list.
- return list;
- }
-}
diff --git a/src/Nitride/Entities/LinkSerialEntitiesValidator.cs b/src/Nitride/Entities/LinkSerialEntitiesValidator.cs
deleted file mode 100644
index d505ced..0000000
--- a/src/Nitride/Entities/LinkSerialEntitiesValidator.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using FluentValidation;
-
-namespace Nitride.Entities;
-
-public class LinkSerialEntitiesValidator : AbstractValidator
-{
- public LinkSerialEntitiesValidator()
- {
- this.RuleFor(x => x.UpdatePreviousNext).NotNull();
- }
-}
diff --git a/tests/Nitride.Tests/Entities/LinkEntitySequenceTests.cs b/tests/Nitride.Tests/Entities/LinkEntitySequenceTests.cs
new file mode 100644
index 0000000..6bb2576
--- /dev/null
+++ b/tests/Nitride.Tests/Entities/LinkEntitySequenceTests.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Gallium;
+
+using Nitride.Entities;
+
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Nitride.Tests.Entities;
+
+public class LinkEntitySequenceTests : NitrideTestBase
+{
+ public LinkEntitySequenceTests(ITestOutputHelper output)
+ : base(output)
+ {
+ }
+
+ [Fact]
+ public void LinkThree()
+ {
+ NitrideTestContext context = this.CreateContext();
+ LinkEntitySequence op = context.Resolve();
+
+ var input = new List
+ {
+ new Entity().Add("page1"),
+ new Entity().Add("page2"),
+ new Entity().Add("page3"),
+ };
+
+ var results = op.Run(input).ToList();
+
+ Assert.Equal(3, results.Count);
+
+ EntitySequence seq1 = results[0].Get();
+ EntitySequence seq2 = results[1].Get();
+ EntitySequence seq3 = results[2].Get();
+
+ Assert.True(seq1.IsFirst);
+ Assert.False(seq1.IsLast);
+ Assert.False(seq1.HasPrevious);
+ Assert.True(seq1.HasNext);
+ Assert.Null(seq1.Previous?.Get());
+ Assert.Equal("page2", seq1.Next!.Get());
+
+ Assert.False(seq2.IsFirst);
+ Assert.False(seq2.IsLast);
+ Assert.True(seq2.HasPrevious);
+ Assert.True(seq2.HasNext);
+ Assert.Equal("page1", seq2.Previous!.Get());
+ Assert.Equal("page3", seq2.Next!.Get());
+
+ Assert.False(seq3.IsFirst);
+ Assert.True(seq3.IsLast);
+ Assert.True(seq3.HasPrevious);
+ Assert.False(seq3.HasNext);
+ Assert.Equal("page2", seq3.Previous!.Get());
+ Assert.Null(seq3.Next?.Get());
+ }
+}