Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="Chinchilla Software Limited">
4 : // // Copyright Chinchilla Software Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Generic;
11 : using System.Linq;
12 : using Cqrs.Domain;
13 : using Cqrs.Events;
14 :
15 : namespace Cqrs.Snapshots
16 : {
17 : /// <summary>
18 : /// An <see cref="ISnapshotStrategy{TAuthenticationToken}"/> that takes a snapshot every 15 versions.
19 : /// </summary>
20 : /// <typeparam name="TAuthenticationToken">The <see cref="Type"/> of the authentication token.</typeparam>
21 : public class DefaultSnapshotStrategy<TAuthenticationToken>
22 : : ISnapshotStrategy<TAuthenticationToken>
23 1 : {
24 : private const int SnapshotInterval = 15;
25 :
26 : /// <summary>
27 : /// Indicates if the <paramref name="aggregateType"/> is able to be snapshotted by checking if the <paramref name="aggregateType"/>
28 : /// directly inherits <see cref="SnapshotAggregateRoot{TAuthenticationToken,TSnapshot}"/>
29 : /// </summary>
30 : /// <param name="aggregateType">The <see cref="Type"/> of <see cref="IAggregateRoot{TAuthenticationToken}"/> to check.</param>
31 1 : public virtual bool IsSnapshotable(Type aggregateType)
32 : {
33 : if (aggregateType.BaseType == null)
34 : return false;
35 : if (aggregateType.BaseType.IsGenericType && aggregateType.BaseType.GetGenericTypeDefinition() == typeof(SnapshotAggregateRoot<,>))
36 : return true;
37 : return IsSnapshotable(aggregateType.BaseType);
38 : }
39 :
40 : /// <summary>
41 : /// Checks <see cref="IsSnapshotable"/> and if it is, also checks if the calculated version number would be exactly dividable by <see cref="GetSnapshotInterval"/>.
42 : /// </summary>
43 : /// <param name="aggregate">The <see cref="IAggregateRoot{TAuthenticationToken}"/> to check.</param>
44 : /// <param name="uncommittedChanges">A collection of uncommited changes to assess. If null the aggregate will be asked to provide them.</param>
45 1 : public virtual bool ShouldMakeSnapShot(IAggregateRoot<TAuthenticationToken> aggregate, IEnumerable<IEvent<TAuthenticationToken>> uncommittedChanges = null)
46 : {
47 : if (!IsSnapshotable(aggregate.GetType()))
48 : return false;
49 :
50 : // The reason this isn't as simple as `(aggregate.Version + aggregate.GetUncommittedChanges().Count()) % SnapshotInterval` is
51 : // because if there are enough uncommited events that this would result in a snapshot, plus some left over, the final state is what will be generated
52 : // when the snapshot is taken, thus this is a faster and more accurate assessment.
53 : int limit = (uncommittedChanges ?? aggregate.GetUncommittedChanges()).Count();
54 : int i = aggregate.Version - limit;
55 : int snapshotInterval = GetSnapshotInterval();
56 :
57 : for (int j = 0; j < limit; j++)
58 : if (++i % snapshotInterval == 0 && i != 0)
59 : return true;
60 : return false;
61 : }
62 :
63 : /// <summary>
64 : /// Returns the value of <see cref="SnapshotInterval"/>.
65 : /// </summary>
66 1 : protected virtual int GetSnapshotInterval()
67 : {
68 : return SnapshotInterval;
69 : }
70 : }
71 : }
|