diff --git a/src/NexusMods.EventSourcing.Abstractions/ACollectionAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/ACollectionAttribute.cs
index 6d840310..884db0a3 100644
--- a/src/NexusMods.EventSourcing.Abstractions/ACollectionAttribute.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/ACollectionAttribute.cs
@@ -6,18 +6,29 @@
namespace NexusMods.EventSourcing.Abstractions;
+///
+/// Abstract base class for an attribute that is a collection of entities.
+///
+///
+///
+///
public class ACollectionAttribute(string name) : IAttribute
where TOwner : IEntity
{
- public bool IsScalar => false;
+ ///
public Type Owner => typeof(TOwner);
+
+ ///
public string Name => name;
+
+ ///
public IAccumulator CreateAccumulator()
{
return new Accumulator();
}
- protected class Accumulator : IAccumulator
+ ///
+ private class Accumulator : IAccumulator
{
private HashSet _values = new();
public void Add(object value)
@@ -50,5 +61,8 @@ public int ReadFrom(ref ReadOnlySpan reader, ISerializationRegistry regist
}
}
+ ///
+ /// The type of the items in the collection.
+ ///
public Type Type => typeof(TType);
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs
index 067706f2..f83d464f 100644
--- a/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs
@@ -9,7 +9,6 @@ namespace NexusMods.EventSourcing.Abstractions.AttributeDefinitions;
///
/// An attribute for the type of an entity.
///
-///
public class TypeAttributeDefinition : IAttribute>
{
///
diff --git a/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs b/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs
index f5b31edb..417d9243 100644
--- a/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs
@@ -84,7 +84,7 @@ public static IServiceCollection AddEntity(this IServiceCollection collection
}
- EntityStructureRegistry.Register(new EntityDefinition(type, entityAttribute.UUID, entityAttribute.Revision));
+ EntityStructureRegistry.Register(new EntityDefinition(type, entityAttribute.Uuid, entityAttribute.Revision));
return collection;
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs
index b1624097..12c62feb 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs
@@ -3,6 +3,9 @@
namespace NexusMods.EventSourcing.Abstractions;
+///
+/// Marks a class as an entity, and sets the UUID and revision of the entity.
+///
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class EntityAttribute : Attribute
{
@@ -11,13 +14,13 @@ public class EntityAttribute : Attribute
/// the revision will cause the entity snapshots to be discarded, and regenerated
/// during the next load.
///
- ///
+ ///
///
public EntityAttribute(string guid, ushort revision)
{
Span span = stackalloc byte[16];
Guid.Parse(guid).TryWriteBytes(span);
- UUID = BinaryPrimitives.ReadUInt128BigEndian(span);
+ Uuid = BinaryPrimitives.ReadUInt128BigEndian(span);
Revision = revision;
}
@@ -29,5 +32,5 @@ public EntityAttribute(string guid, ushort revision)
///
/// The unique identifier of the entity *Type*.
///
- public UInt128 UUID { get; }
+ public UInt128 Uuid { get; }
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/EntityAttributeDefinition.cs
index 8d7c8e36..ce8cfaa0 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityAttributeDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityAttributeDefinition.cs
@@ -4,7 +4,7 @@ namespace NexusMods.EventSourcing.Abstractions;
///
/// A attribute that contains a lazy reference to another entity. The link can be updated via setting the entity Id, and
-/// retrieved via the method. Think of this attribute like a foreign key in a database.
+/// retrieved via the method. Think of this attribute like a foreign key in a database.
///
///
///
@@ -51,9 +51,7 @@ public void Unlink(TContext context, EntityId owner)
///
/// Gets the value of the attribute for the given entity.
///
- ///
///
- ///
///
public TOther Get(TOwner owner)
{
@@ -63,7 +61,10 @@ public TOther Get(TOwner owner)
throw new InvalidOperationException($"Attribute not found for {Name} on {Owner.Name} with id {owner.Id}");
}
+ ///
public Type Owner => typeof(TOwner);
+
+ ///
public string Name => attrName;
IAccumulator IAttribute.CreateAccumulator()
{
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityCollectionAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/EntityCollectionAttributeDefinition.cs
index a4ba52fa..b53d51cf 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityCollectionAttributeDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityCollectionAttributeDefinition.cs
@@ -1,5 +1,6 @@
namespace NexusMods.EventSourcing.Abstractions;
+///
public class EntityCollectionAttributeDefinition(string name) : ACollectionAttribute(name) where TOwner : IEntity
where TEntity : IEntity
{
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityId.cs b/src/NexusMods.EventSourcing.Abstractions/EntityId.cs
index 5757a33f..1d37f88d 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityId.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityId.cs
@@ -5,11 +5,23 @@
namespace NexusMods.EventSourcing.Abstractions;
+///
+/// A unique identifier for an .
+///
[ValueObject]
public readonly partial struct EntityId
{
+ ///
+ /// Casts this to a of the specified type.
+ ///
+ ///
+ ///
public EntityId Cast() where T : IEntity => new(this);
+ ///
+ /// Creates a random .
+ ///
+ ///
public static EntityId NewId()
{
var guid = Guid.NewGuid();
@@ -19,8 +31,17 @@ public static EntityId NewId()
return From(value);
}
+ ///
+ /// Reads the from the specified .
+ ///
+ ///
+ ///
public static EntityId From(ReadOnlySpan data) => new(BinaryPrimitives.ReadUInt128BigEndian(data));
+ ///
+ /// Writes the to the specified .
+ ///
+ ///
public void TryWriteBytes(Span span)
{
BinaryPrimitives.WriteUInt128BigEndian(span, Value);
@@ -95,7 +116,7 @@ public override string ToString()
///
/// Converts the to a of another type.
///
- ///
+ ///
///
- public EntityId Cast() where T : IEntity => new(Value);
+ public EntityId Cast() where TTo : IEntity => new(Value);
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityIdDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/EntityIdDefinition.cs
index d9e6f042..723e1114 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityIdDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityIdDefinition.cs
@@ -6,6 +6,9 @@
namespace NexusMods.EventSourcing.Abstractions;
+///
+/// A internal class used for creating secondary indexes on entity Ids,
+///
public class EntityIdDefinition : IAttribute, IIndexableAttribute
{
///
@@ -31,6 +34,7 @@ public void WriteTo(Span span, EntityId value)
BinaryPrimitives.WriteUInt128BigEndian(span, value.Value);
}
+ ///
public bool Equal(IAccumulator accumulator, EntityId val)
{
return ((EntityIdDefinitionAccumulator)accumulator).Id == val;
@@ -61,10 +65,17 @@ public void WriteTo(Span span, IAccumulator accumulator)
}
}
+///
+/// Accumulator for the attribute.
+///
public class EntityIdDefinitionAccumulator : IAccumulator
{
+ ///
+ /// The Id of the entity.
+ ///
public EntityId Id;
+ ///
public void WriteTo(IBufferWriter writer, ISerializationRegistry registry)
{
var span = writer.GetSpan(16);
@@ -72,6 +83,7 @@ public void WriteTo(IBufferWriter writer, ISerializationRegistry registry)
writer.Advance(16);
}
+ ///
public int ReadFrom(ref ReadOnlySpan data, ISerializationRegistry registry)
{
Id = EntityId.From(data);
diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs b/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs
index 6088c4bd..f1a77297 100644
--- a/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs
@@ -60,9 +60,6 @@ public static IEnumerable AllIndexableAttributes()
///
/// Registers an entity type in the global registry.
///
- ///
- ///
- ///
public static void Register(EntityDefinition definition)
{
_entityDefinitionsByType.TryAdd(definition.Type, definition);
@@ -73,6 +70,7 @@ public static void Register(EntityDefinition definition)
/// Returns all attributes for the given entity type.
///
///
+ ///
///
public static bool TryGetAttributes(Type owner, [NotNullWhen(true)] out ConcurrentDictionary? result)
{
diff --git a/src/NexusMods.EventSourcing.Abstractions/IAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/IAttribute.cs
index b6392465..c550c88f 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IAttribute.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IAttribute.cs
@@ -36,6 +36,6 @@ public interface IAttribute : IAttribute where TAccumulator : IAcc
/// to lazily create accumulators.
///
///
- public TAccumulator CreateAccumulator();
+ public new TAccumulator CreateAccumulator();
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/IEntity.cs b/src/NexusMods.EventSourcing.Abstractions/IEntity.cs
index 7bafdf93..95ddb0f6 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IEntity.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IEntity.cs
@@ -21,7 +21,7 @@ public interface IEntity : INotifyPropertyChanged
///
- /// The type descriptor for all entities. Emitted by the method.
+ /// The type descriptor for all entities. Emitted by .New on the EntityContext
///
public static readonly TypeAttributeDefinition TypeAttribute = new();
diff --git a/src/NexusMods.EventSourcing.Abstractions/IEntityContext.cs b/src/NexusMods.EventSourcing.Abstractions/IEntityContext.cs
index 0aea13b3..5ec1069b 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IEntityContext.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IEntityContext.cs
@@ -46,7 +46,6 @@ public interface IEntityContext
///
///
///
- ///
///
///
///
diff --git a/src/NexusMods.EventSourcing.Abstractions/IEventContext.cs b/src/NexusMods.EventSourcing.Abstractions/IEventContext.cs
index 5490cb6d..4df5bb56 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IEventContext.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IEventContext.cs
@@ -13,7 +13,6 @@ public interface IEventContext
/// Gets the accumulator for the given attribute definition, if the accumulator does not exist it will be created. If
/// the context is not setup for this entityId then false will be returned and the accumulator should be ignored.
///
- ///
///
///
///
diff --git a/src/NexusMods.EventSourcing.Abstractions/IIndexableAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/IIndexableAttribute.cs
index ac621e14..f495ab4a 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IIndexableAttribute.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IIndexableAttribute.cs
@@ -30,6 +30,7 @@ public interface IIndexableAttribute : IAttribute
/// Writes the accumulator to the given span, which will be the size returned by .
///
///
+ ///
public void WriteTo(Span span, IAccumulator accumulator);
}
@@ -42,7 +43,7 @@ public interface IIndexableAttribute : IAttribute
public interface IIndexableAttribute : IIndexableAttribute
{
///
- /// Writes the accumulator to the given span, which will be the size returned by .
+ /// Writes the accumulator to the given span, which will be the size returned by .
///
///
///
@@ -54,7 +55,6 @@ public interface IIndexableAttribute : IIndexableAttribute
///
///
///
- ///
///
bool Equal(IAccumulator accumulator, TVal val);
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/ISingletonEntity.cs b/src/NexusMods.EventSourcing.Abstractions/ISingletonEntity.cs
index 82d90bc9..30059338 100644
--- a/src/NexusMods.EventSourcing.Abstractions/ISingletonEntity.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/ISingletonEntity.cs
@@ -5,5 +5,8 @@ namespace NexusMods.EventSourcing.Abstractions;
///
public interface ISingletonEntity : IEntity
{
+ ///
+ /// The singleton id of the entity.
+ ///
public static virtual EntityId SingletonId { get; }
}
diff --git a/src/NexusMods.EventSourcing.Abstractions/IndexedMultiEntityAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/IndexedMultiEntityAttributeDefinition.cs
index 138655a4..ae6163b0 100644
--- a/src/NexusMods.EventSourcing.Abstractions/IndexedMultiEntityAttributeDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/IndexedMultiEntityAttributeDefinition.cs
@@ -20,11 +20,20 @@ public class IndexedMultiEntityAttributeDefinition(string
///
public string Name => name;
+ ///
public IndexedMultiEntityAccumulator CreateAccumulator()
{
return new IndexedMultiEntityAccumulator();
}
+ ///
+ /// Adds a new entity to the collection.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public void Add(TContext context, EntityId owner, TKey key, EntityId value)
where TContext : IEventContext
{
@@ -61,6 +70,11 @@ public Dictionary> Get(TOwner entity)
}
}
+///
+/// Accumulator for the attribute.
+///
+///
+///
public class IndexedMultiEntityAccumulator : IAccumulator
where TOther : AEntity
where TKey : notnull
@@ -68,6 +82,7 @@ public class IndexedMultiEntityAccumulator : IAccumulator
internal Dictionary> _values = new();
internal Dictionary, TKey> _keys = new();
+ ///
public void WriteTo(IBufferWriter writer, ISerializationRegistry registry)
{
var getSpan = writer.GetSpan(2);
@@ -81,6 +96,7 @@ public void WriteTo(IBufferWriter writer, ISerializationRegistry registry)
}
}
+ ///
public int ReadFrom(ref ReadOnlySpan input, ISerializationRegistry registry)
{
var originalSize = input.Length;
diff --git a/src/NexusMods.EventSourcing.Abstractions/MultiEntityAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/MultiEntityAttributeDefinition.cs
index c6f6a9ff..c21665c5 100644
--- a/src/NexusMods.EventSourcing.Abstractions/MultiEntityAttributeDefinition.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/MultiEntityAttributeDefinition.cs
@@ -25,8 +25,6 @@ public class MultiEntityAttributeDefinition : IAttribute
///
- ///
- ///
public MultiEntityAttributeDefinition(string name)
{
_name = name;
@@ -58,7 +56,7 @@ public void Add(TContext context, EntityId owner, EntityId
///
///
- ///
+ ///
///
public void AddAll(TContext context, EntityId owner, EntityId[] values)
where TContext : IEventContext
@@ -129,6 +127,10 @@ public class MultiEntityAccumulator : IAccumulator
///
public ReadOnlyObservableCollection? TransformedReadOnly = null;
+ ///
+ /// Initializes the accumulator
+ ///
+ ///
public void Init(IEntityContext context)
{
if (Transformed != null)
diff --git a/src/NexusMods.EventSourcing.Abstractions/ScalarAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/ScalarAttribute.cs
index 6f23b7df..d3b72cb5 100644
--- a/src/NexusMods.EventSourcing.Abstractions/ScalarAttribute.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/ScalarAttribute.cs
@@ -59,9 +59,7 @@ public void Unset(TContext context, EntityId owner)
///
/// Gets the value of the attribute for the given entity.
///
- ///
///
- ///
///
public TType Get(TOwner owner)
{
diff --git a/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializationRegistry.cs b/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializationRegistry.cs
index f6b91bd7..42913bf3 100644
--- a/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializationRegistry.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializationRegistry.cs
@@ -36,6 +36,7 @@ public interface ISerializationRegistry
/// Deserializes the given bytes into the given type, returning the number of bytes read.
///
///
+ ///
///
///
public int Deserialize(ReadOnlySpan bytes, out TVal value);
diff --git a/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializer.cs b/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializer.cs
index a1e5f46b..acb592cd 100644
--- a/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializer.cs
+++ b/src/NexusMods.EventSourcing.Abstractions/Serialization/ISerializer.cs
@@ -35,7 +35,7 @@ public interface IFixedSizeSerializer : ISerializer
{
///
/// Serialize the given value into the given span. The span will be exactly the size of the value returned
- /// by .
+ /// by .
///
///
///
diff --git a/src/NexusMods.EventSourcing/AEventStore.cs b/src/NexusMods.EventSourcing/AEventStore.cs
index 66b41e01..14ae995a 100644
--- a/src/NexusMods.EventSourcing/AEventStore.cs
+++ b/src/NexusMods.EventSourcing/AEventStore.cs
@@ -7,6 +7,9 @@
namespace NexusMods.EventSourcing;
+///
+/// Abstract event store implementation, provides helpers for serialization and deserialization.
+///
public abstract class AEventStore : IEventStore
{
private readonly IVariableSizeSerializer _stringSerializer;
@@ -14,6 +17,10 @@ public abstract class AEventStore : IEventStore
private readonly ISerializationRegistry _serializationRegistry;
private readonly PooledMemoryBufferWriter _writer;
+ ///
+ /// Abstract event store implementation, provides helpers for serialization and deserialization.
+ ///
+ ///
public AEventStore(ISerializationRegistry serializationRegistry)
{
_stringSerializer = (serializationRegistry.GetSerializer(typeof(string)) as IVariableSizeSerializer)!;
@@ -23,6 +30,14 @@ public AEventStore(ISerializationRegistry serializationRegistry)
}
+ ///
+ /// Deserializes a snapshot into a definition and a list of attributes.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
protected bool DeserializeSnapshot(out IAccumulator loadedDefinition,
out (IAttribute Attribute, IAccumulator Accumulator)[] loadedAttributes, ReadOnlySpan snapshot)
{
@@ -72,6 +87,12 @@ protected bool DeserializeSnapshot(out IAccumulator loadedDefinition,
return true;
}
+ ///
+ /// Serializes a snapshot into a byte span, the span will be valid until the next call to a serialize method.
+ ///
+ ///
+ ///
+ ///
protected ReadOnlySpan SerializeSnapshot(EntityId id, IDictionary attributes)
{
_writer.Reset();
@@ -105,12 +126,17 @@ public abstract TransactionId Add(TEntity eventEntity, TColl ind
where TEntity : IEvent
where TColl : IList<(IIndexableAttribute, IAccumulator)>;
+ ///
public abstract void EventsForIndex(IIndexableAttribute attr, TVal value, TIngester ingester, TransactionId fromTx,
TransactionId toTx) where TIngester : IEventIngester;
+ ///
public abstract TransactionId GetSnapshot(TransactionId asOf, EntityId entityId, out IAccumulator loadedDefinition,
out (IAttribute Attribute, IAccumulator Accumulator)[] loadedAttributes);
+ ///
public abstract void SetSnapshot(TransactionId txId, EntityId id, IDictionary attributes);
+
+ ///
public abstract TransactionId TxId { get; }
}
diff --git a/src/NexusMods.EventSourcing/EntityContext.cs b/src/NexusMods.EventSourcing/EntityContext.cs
index d2afa966..30f1ab98 100644
--- a/src/NexusMods.EventSourcing/EntityContext.cs
+++ b/src/NexusMods.EventSourcing/EntityContext.cs
@@ -8,21 +8,29 @@
namespace NexusMods.EventSourcing;
+///
+/// The primary way of interacting with the events. This class is thread safe, and can be used to lookup entities
+/// and insert new events.
+///
+///
public class EntityContext(IEventStore store) : IEntityContext
{
- public const int MaxEventsBeforeSnapshotting = 250;
+ ///
+ /// The maximum number of events that can be processed before a snapshot is taken.
+ ///
+ private const int MaxEventsBeforeSnapshotting = 250;
- private TransactionId asOf = store.TxId;
- private object _lock = new();
+ private TransactionId _asOf = store.TxId;
+ private readonly object _lock = new();
- private IndexerIngester _indexerIngester = new();
- private List<(IIndexableAttribute, IAccumulator)> _indexUpdaters = new();
- private HashSet<(EntityId, string)> _updatedAttributes = new();
+ private readonly IndexerIngester _indexerIngester = new();
+ private readonly List<(IIndexableAttribute, IAccumulator)> _indexUpdaters = new();
+ private readonly HashSet<(EntityId, string)> _updatedAttributes = new();
- private ConcurrentDictionary _entities = new();
- private ConcurrentDictionary> _values = new();
+ private readonly ConcurrentDictionary _entities = new();
+ private readonly ConcurrentDictionary> _values = new();
- private ObjectPool _definitionAccumulatorPool =
+ private readonly ObjectPool _definitionAccumulatorPool =
new DefaultObjectPool(new DefaultPooledObjectPolicy());
///
@@ -71,7 +79,7 @@ private Dictionary LoadAccumulators(EntityId id)
{
var values = new Dictionary();
- var snapshotTxId = store.GetSnapshot(asOf, id, out var loadedDefinition, out var loadedAttributes);
+ var snapshotTxId = store.GetSnapshot(_asOf, id, out var loadedDefinition, out var loadedAttributes);
if (snapshotTxId != TransactionId.Min)
{
@@ -81,7 +89,7 @@ private Dictionary LoadAccumulators(EntityId id)
}
var ingester = new EntityContextIngester(values, id);
- store.EventsForIndex(IEntity.EntityIdAttribute, id, ingester, snapshotTxId, asOf);
+ store.EventsForIndex(IEntity.EntityIdAttribute, id, ingester, snapshotTxId, _asOf);
if (ingester.ProcessedEvents > MaxEventsBeforeSnapshotting)
{
@@ -115,6 +123,13 @@ public TEntity Get() where TEntity : ISingletonEntity
return (TEntity)_entities[id];
}
+ ///
+ /// Adds a new event to the store, and updates the cache. This method is thread safe as the system is considered
+ /// single-writer, multiple-reader.
+ ///
+ ///
+ ///
+ ///
public TransactionId Add(TEvent newEvent) where TEvent : IEvent
{
lock (_lock)
@@ -155,7 +170,7 @@ public TransactionId Add(TEvent newEvent) where TEvent : IEvent
}
// Update the asOf transaction id
- asOf = newId;
+ _asOf = newId;
// Clear the updated attributes
_updatedAttributes.Clear();
@@ -221,11 +236,19 @@ public void EmptyCaches()
_values.Clear();
}
+ ///
+ /// Gets all the entities that have the given attribute set to the given value.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public IEnumerable EntitiesForIndex(IIndexableAttribute attr, TVal val)
where TEntity : IEntity
{
var foundEntities = new HashSet>();
- store.EventsForIndex(attr, val, new SecondaryIndexIngester(foundEntities), TransactionId.Min, asOf);
+ store.EventsForIndex(attr, val, new SecondaryIndexIngester(foundEntities), TransactionId.Min, _asOf);
foreach (var entityId in foundEntities)
{
@@ -243,7 +266,7 @@ public IEnumerable EntitiesForIndex(IIndexableAttribute<
}
}
- private struct SecondaryIndexIngester(HashSet> foundEntities) : IEventIngester,
+ private readonly struct SecondaryIndexIngester(HashSet> foundEntities) : IEventIngester,
IEventContext where TType : IEntity
{
@@ -263,7 +286,5 @@ public bool GetAccumulator(EntityId en
accumulator = default!;
return false;
}
-
-
}
}
diff --git a/src/NexusMods.EventSourcing/EntityContextIngester.cs b/src/NexusMods.EventSourcing/EntityContextIngester.cs
index b2ce94ad..cfe13b13 100644
--- a/src/NexusMods.EventSourcing/EntityContextIngester.cs
+++ b/src/NexusMods.EventSourcing/EntityContextIngester.cs
@@ -5,6 +5,11 @@
namespace NexusMods.EventSourcing;
+///
+/// An ingester that can be used to ingest events into an entity context.
+///
+///
+///
public class EntityContextIngester(Dictionary values, EntityId id) : IEventContext, IEventIngester
{
///
diff --git a/src/NexusMods.EventSourcing/ForwardEventContext.cs b/src/NexusMods.EventSourcing/ForwardEventContext.cs
index 5a7982f1..b921cc25 100644
--- a/src/NexusMods.EventSourcing/ForwardEventContext.cs
+++ b/src/NexusMods.EventSourcing/ForwardEventContext.cs
@@ -9,7 +9,6 @@ namespace NexusMods.EventSourcing;
/// An event context that forwards events to a dictionary of accumulators, and is tuned for adding new values to existing
/// accumulators and entities. In other words is for moving a
///
-///
public readonly struct ForwardEventContext(ConcurrentDictionary> trackedEntities, HashSet<(EntityId, string)> updatedAttributes) : IEventContext
{
///
diff --git a/src/NexusMods.EventSourcing/IndexerIngester.cs b/src/NexusMods.EventSourcing/IndexerIngester.cs
index 80855a62..0e5dd4b0 100644
--- a/src/NexusMods.EventSourcing/IndexerIngester.cs
+++ b/src/NexusMods.EventSourcing/IndexerIngester.cs
@@ -4,19 +4,24 @@
namespace NexusMods.EventSourcing;
+///
+/// An ingester that indexes all the attributes of all the entities in the transaction.
+///
public class IndexerIngester : IEventIngester, IEventContext
{
- public Dictionary> IndexedAttributes = new();
+ public readonly Dictionary> IndexedAttributes = new();
- public HashSet Ids = new();
+ public readonly HashSet Ids = new();
+ ///
public bool Ingest(TransactionId id, IEvent @event)
{
@event.Apply(this);
return true;
}
+ ///
public bool GetAccumulator(EntityId entityId, TAttribute attributeDefinition,
out TAccumulator accumulator) where TOwner : IEntity where TAttribute : IAttribute where TAccumulator : IAccumulator
{
diff --git a/src/NexusMods.EventSourcing/PooledMemoryBufferWriter.cs b/src/NexusMods.EventSourcing/PooledMemoryBufferWriter.cs
index cd1305ee..629c6b94 100644
--- a/src/NexusMods.EventSourcing/PooledMemoryBufferWriter.cs
+++ b/src/NexusMods.EventSourcing/PooledMemoryBufferWriter.cs
@@ -5,6 +5,9 @@
namespace NexusMods.EventSourcing;
+///
+/// A IBufferWriter that uses pooled memory to reduce allocations.
+///
public sealed class PooledMemoryBufferWriter : IBufferWriter
{
private IMemoryOwner _owner;
@@ -12,6 +15,10 @@ public sealed class PooledMemoryBufferWriter : IBufferWriter
private int _idx;
private int _size;
+ ///
+ /// Constructs a new pooled memory buffer writer, with the given initial capacity.
+ ///
+ ///
public PooledMemoryBufferWriter(int initialCapacity = 1024)
{
_owner = MemoryPool.Shared.Rent(initialCapacity);
@@ -20,11 +27,18 @@ public PooledMemoryBufferWriter(int initialCapacity = 1024)
_size = initialCapacity;
}
+ ///
+ /// Resets the buffer writer, allowing it to be reused.
+ ///
public void Reset()
{
_idx = 0;
}
+ ///
+ /// Gets the written span.
+ ///
+ ///
public ReadOnlySpan GetWrittenSpan() => _data.Span.SliceFast(0, _idx);
private void Expand()
@@ -39,6 +53,7 @@ private void Expand()
}
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
@@ -47,6 +62,7 @@ public void Advance(int count)
_idx += count;
}
+ ///
public Memory GetMemory(int sizeHint = 0)
{
if (_idx + sizeHint > _size)
@@ -54,6 +70,7 @@ public Memory GetMemory(int sizeHint = 0)
return _data[_idx..];
}
+ ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span GetSpan(int sizeHint = 0)
{
diff --git a/src/NexusMods.EventSourcing/Serialization/ArraySerializer.cs b/src/NexusMods.EventSourcing/Serialization/ArraySerializer.cs
index fcc6528e..c1a1e386 100644
--- a/src/NexusMods.EventSourcing/Serialization/ArraySerializer.cs
+++ b/src/NexusMods.EventSourcing/Serialization/ArraySerializer.cs
@@ -7,19 +7,25 @@
namespace NexusMods.EventSourcing.Serialization;
+///
+/// A serializer for arrays of any type supported by the registry.
+///
public class GenericArraySerializer : IGenericSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
return false;
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}
+ ///
public bool TrySpecialize(Type baseType, Type[] argTypes, Func serializerFinder, [NotNullWhen(true)] out ISerializer? serializer)
{
if (!baseType.IsArray)
@@ -40,16 +46,24 @@ public bool TrySpecialize(Type baseType, Type[] argTypes, Func).MakeGenericType(itemType, itemSerializer.GetType());
- serializer = (ISerializer) Activator.CreateInstance(type, itemSerializer, itemSize)!;
+ serializer = (ISerializer) Activator.CreateInstance(type, itemSerializer)!;
return true;
}
}
}
+///
+/// Specialized serializer for arrays of a given type, where the type is of a fixed size.
+///
+///
+///
+///
+///
public class FixedItemSizeArraySerializer(TItemSerializer itemSerializer, int itemSize) : IVariableSizeSerializer
where TItemSerializer : IFixedSizeSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
if (!valueType.IsArray)
@@ -60,12 +74,14 @@ public bool CanSerialize(Type valueType)
return itemSerializer.CanSerialize(valueType.GetElementType()!);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}
+ ///
public void Serialize(TItem[] value, TWriter output) where TWriter : IBufferWriter
{
var totalSize = sizeof(ushort) + (itemSize * value.Length);
@@ -81,6 +97,7 @@ public void Serialize(TItem[] value, TWriter output) where TWriter : IB
output.Advance(totalSize);
}
+ ///
public int Deserialize(ReadOnlySpan from, out TItem[] value)
{
var size = BinaryPrimitives.ReadUInt16BigEndian(from);
@@ -97,10 +114,16 @@ public int Deserialize(ReadOnlySpan from, out TItem[] value)
}
}
-
-public class VariableItemSizeSerializer(TItemSerializer itemSerializer, int itemSize) : IVariableSizeSerializer
+///
+/// Specialized serializer for arrays of a given type, where the type is of a variable size.
+///
+///
+///
+///
+public class VariableItemSizeSerializer(TItemSerializer itemSerializer) : IVariableSizeSerializer
where TItemSerializer : IVariableSizeSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
if (!valueType.IsArray)
@@ -111,12 +134,14 @@ public bool CanSerialize(Type valueType)
return itemSerializer.CanSerialize(valueType.GetElementType()!);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}
+ ///
public void Serialize(TItem[] value, TWriter output) where TWriter : IBufferWriter
{
var span = output.GetSpan(sizeof(ushort));
@@ -129,6 +154,7 @@ public void Serialize(TItem[] value, TWriter output) where TWriter : IB
}
}
+ ///
public int Deserialize(ReadOnlySpan from, out TItem[] value)
{
var size = BinaryPrimitives.ReadUInt16BigEndian(from);
diff --git a/src/NexusMods.EventSourcing/Serialization/BinaryEventSerializer.cs b/src/NexusMods.EventSourcing/Serialization/BinaryEventSerializer.cs
index d670a943..2a62499f 100644
--- a/src/NexusMods.EventSourcing/Serialization/BinaryEventSerializer.cs
+++ b/src/NexusMods.EventSourcing/Serialization/BinaryEventSerializer.cs
@@ -15,6 +15,9 @@ namespace NexusMods.EventSourcing.Serialization;
using MemberDefinition = (ParameterInfo Ref, ParameterInfo Base, ParameterExpression Variable, ISerializer Serializer);
+///
+/// Serializer for events, this assumes all events inherit from IEvent, and are records. Polymorphism is supported.
+///
public sealed class BinaryEventSerializer : IEventSerializer, IVariableSizeSerializer
{
private readonly PooledMemoryBufferWriter _writer;
@@ -30,6 +33,11 @@ public sealed class BinaryEventSerializer : IEventSerializer, IVariableSizeSeria
private delegate int EventDeserializerDelegate(ReadOnlySpan data, out IEvent @event);
+ ///
+ /// DI Constructor.
+ ///
+ ///
+ ///
public BinaryEventSerializer(ISerializationRegistry registry, IEnumerable eventDefinitions)
{
_serializerRegistry = registry;
@@ -49,6 +57,7 @@ private void PopulateSerializers(EventDefinition[] eventDefinitions)
}
+ ///
public ReadOnlySpan Serialize(IEvent @event)
{
_writer.Reset();
@@ -56,6 +65,7 @@ public ReadOnlySpan Serialize(IEvent @event)
return _writer.GetWrittenSpan();
}
+ ///
public IEvent Deserialize(ReadOnlySpan data)
{
var id = BinaryPrimitives.ReadUInt128BigEndian(data);
@@ -178,8 +188,6 @@ private EventDeserializerDelegate BuildVariableSizeDeserializer(EventDefinition
var spanParam = Expression.Parameter(typeof(ReadOnlySpan));
- var ctorExpressions = new List();
-
var offsetVariable = Expression.Variable(typeof(int), "offset");
var blockExprs = new List
@@ -406,22 +414,26 @@ private EventSerializerDelegate BuildFixedSizeSerializer(EventDefinition eventDe
}
+ ///
public bool CanSerialize(Type valueType)
{
return valueType == typeof(IEvent);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}
+ ///
public void Serialize(IEvent value, TWriter output) where TWriter : IBufferWriter
{
_serializerDelegates[value.GetType()](value);
}
+ ///
public int Deserialize(ReadOnlySpan from, out IEvent value)
{
var used = _deserializerDelegates[BinaryPrimitives.ReadUInt128BigEndian(from)](SliceFastStart(from, 16), out value);
diff --git a/src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs b/src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs
index dcd097a6..8a809fa9 100644
--- a/src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs
+++ b/src/NexusMods.EventSourcing/Serialization/BoolSerializer.cs
@@ -3,24 +3,31 @@
namespace NexusMods.EventSourcing.Serialization;
+///
+/// Serializer for bools.
+///
public class BoolSerializer : IFixedSizeSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
return valueType == typeof(bool);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = sizeof(bool);
return valueType == typeof(bool);
}
+ ///
public void Serialize(bool value, Span output)
{
output[0] = value ? (byte)1 : (byte)0;
}
+ ///
public bool Deserialize(ReadOnlySpan from)
{
return from[0] == 1;
diff --git a/src/NexusMods.EventSourcing/Serialization/EntityIdSerializer.cs b/src/NexusMods.EventSourcing/Serialization/EntityIdSerializer.cs
index 04b547fa..3f544594 100644
--- a/src/NexusMods.EventSourcing/Serialization/EntityIdSerializer.cs
+++ b/src/NexusMods.EventSourcing/Serialization/EntityIdSerializer.cs
@@ -6,43 +6,56 @@
namespace NexusMods.EventSourcing.Serialization;
+///
+/// Serializer for EntityIds.
+///
public class EntityIdSerializer : IFixedSizeSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
return valueType == typeof(EntityId);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 16;
return true;
}
+ ///
public void Serialize(EntityId value, Span output)
{
value.TryWriteBytes(output);
}
+ ///
public EntityId Deserialize(ReadOnlySpan from)
{
return EntityId.From(from);
}
}
+///
+/// Serializer for typed EntityIds.
+///
public class GenericEntityIdSerializer : IGenericSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
return false;
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 0;
return false;
}
+ ///
public bool TrySpecialize(Type baseType, Type[] argTypes, Func serializerFinder, [NotNullWhen(true)] out ISerializer? serializer)
{
if (baseType != typeof(EntityId<>) || argTypes.Length != 1)
diff --git a/src/NexusMods.EventSourcing/Serialization/GuidSerializer.cs b/src/NexusMods.EventSourcing/Serialization/GuidSerializer.cs
index 31d9c205..9b73cb30 100644
--- a/src/NexusMods.EventSourcing/Serialization/GuidSerializer.cs
+++ b/src/NexusMods.EventSourcing/Serialization/GuidSerializer.cs
@@ -3,24 +3,31 @@
namespace NexusMods.EventSourcing.Serialization;
+///
+/// Serializer for Guids.
+///
public sealed class GuidSerializer : IFixedSizeSerializer
{
+ ///
public bool CanSerialize(Type valueType)
{
return valueType == typeof(Guid);
}
+ ///
public bool TryGetFixedSize(Type valueType, out int size)
{
size = 16;
return true;
}
+ ///
public void Serialize(Guid value, Span output)
{
value.TryWriteBytes(output);
}
+ ///
public Guid Deserialize(ReadOnlySpan from)
{
return new(from);
diff --git a/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs b/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs
index b3b448a2..7d83eeda 100644
--- a/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs
+++ b/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs
@@ -33,7 +33,6 @@ public SerializationRegistry(IEnumerable diInjectedSerializers)
/// Gets a serializer that can serialize the given type.
///
///
- /// Called when the cache needs to recursively create another type serializer
///
///
public ISerializer GetSerializer(Type type)