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.Configuration;
11 : using System.Data.Linq;
12 : using System.Linq;
13 : using cdmdotnet.Logging;
14 : using Cqrs.Configuration;
15 : using Cqrs.Domain;
16 : using Cqrs.Exceptions;
17 : using Cqrs.Snapshots;
18 :
19 : namespace Cqrs.Events
20 : {
21 : /// <summary>
22 : /// Stores the most recent <see cref="Snapshot">snapshots</see> for replay and <see cref="IAggregateRoot{TAuthenticationToken}"/> rehydration on a <see cref="SnapshotAggregateRoot{TAuthenticationToken,TSnapshot}"/> in SqlServer that uses LinqToSql and follows a rigid schema.
23 : /// </summary>
24 : public class SqlSnapshotStore
25 : : SnapshotStore
26 1 : {
27 : internal const string SqlSnapshotStoreConnectionNameApplicationKey = @"Cqrs.SqlSnapshotStore.ConnectionStringName";
28 :
29 : internal const string SqlSnapshotStoreTableNameApplicationKeyPattern = @"Cqrs.SqlSnapshotStore.CustomTableNames.{0}";
30 :
31 : /// <summary>
32 : /// Instantiate a new instance of the <see cref="SqlSnapshotStore"/> class.
33 : /// </summary>
34 1 : public SqlSnapshotStore(IConfigurationManager configurationManager, ISnapshotDeserialiser eventDeserialiser, ILogger logger, ICorrelationIdHelper correlationIdHelper, ISnapshotBuilder snapshotBuilder)
35 : : base(configurationManager, eventDeserialiser, snapshotBuilder, logger, correlationIdHelper)
36 : {
37 : }
38 :
39 : #region Implementation of ISnapshotStore
40 :
41 : /// <summary>
42 : /// Get the latest <see cref="Snapshot"/> from storage.
43 : /// </summary>
44 : /// <returns>The most recent <see cref="Snapshot"/> of</returns>
45 1 : protected override Snapshot Get(Type aggregateRootType, string streamName)
46 : {
47 : using (DataContext dbDataContext = CreateDbDataContext(aggregateRootType.FullName))
48 : {
49 : EventData query = GetEventStoreSnapshotTable(dbDataContext)
50 : .AsQueryable()
51 : .Where(snapshot => snapshot.AggregateId == streamName)
52 : .OrderByDescending(eventData => eventData.Version)
53 : .Take(1)
54 : .SingleOrDefault();
55 :
56 : if (query == null)
57 : return null;
58 : return EventDeserialiser.Deserialise(query);
59 : }
60 : }
61 :
62 : /// <summary>
63 : /// Saves the provided <paramref name="snapshot"/> into storage.
64 : /// </summary>
65 : /// <param name="snapshot">the <see cref="Snapshot"/> to save and store.</param>
66 1 : public override void Save(Snapshot snapshot)
67 : {
68 : using (DataContext dbDataContext = CreateDbDataContext(snapshot.GetType().Name))
69 : {
70 : Add(dbDataContext, snapshot);
71 : }
72 : }
73 :
74 : #endregion
75 :
76 : /// <summary>
77 : /// Creates a new <see cref="DataContext"/> using connection string settings from ConfigurationManager.
78 : /// </summary>
79 1 : protected virtual DataContext CreateDbDataContext(string aggregateRootTypeName = null)
80 : {
81 : string connectionStringKey;
82 : string applicationKey;
83 : if (!ConfigurationManager.TryGetSetting(SqlSnapshotStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
84 : throw new MissingApplicationSettingForConnectionStringException(SqlSnapshotStoreConnectionNameApplicationKey);
85 : ConnectionStringSettings connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey];
86 : if (connectionString == null)
87 : throw new MissingConnectionStringException(applicationKey);
88 : connectionStringKey = connectionString.ConnectionString;
89 :
90 : string tableName;
91 : if (!string.IsNullOrWhiteSpace(aggregateRootTypeName) && ConfigurationManager.TryGetSetting(string.Format(SqlSnapshotStoreTableNameApplicationKeyPattern, aggregateRootTypeName), out tableName) && !string.IsNullOrEmpty(tableName))
92 : {
93 : bool autoname;
94 : if (bool.TryParse(tableName, out autoname))
95 : {
96 : if (autoname)
97 : return SqlEventStoreDataContext.New<EventData>(aggregateRootTypeName.Replace(".", "_"), connectionStringKey);
98 : }
99 : else
100 : return SqlEventStoreDataContext.New<EventData>(tableName, connectionStringKey);
101 : }
102 :
103 : return SqlEventStoreDataContext.New<EventData>("Snapshots", connectionStringKey);
104 : }
105 :
106 : /// <summary>
107 : /// Gets the <see cref="Table{TEntity}"/> of <see cref="Snapshot"/>.
108 : /// </summary>
109 : /// <param name="dbDataContext">The <see cref="DataContext"/> to use.</param>
110 1 : protected virtual Table<EventData> GetEventStoreSnapshotTable(DataContext dbDataContext)
111 : {
112 : // Get a typed table to run queries.
113 : return dbDataContext.GetTable<EventData>();
114 : }
115 :
116 : /// <summary>
117 : /// Persist the provided <paramref name="snapshot"/> into SQL Server using the provided <paramref name="dbDataContext"/>.
118 : /// </summary>
119 1 : protected virtual void Add(DataContext dbDataContext, Snapshot snapshot)
120 : {
121 : Logger.LogDebug("Adding data to the SQL snapshot database", "SqlSnapshotStore\\Add");
122 : try
123 : {
124 : DateTime start = DateTime.Now;
125 : EventData eventData = BuildEventData(snapshot);
126 : GetEventStoreSnapshotTable(dbDataContext).InsertOnSubmit(eventData);
127 : dbDataContext.SubmitChanges();
128 : DateTime end = DateTime.Now;
129 : Logger.LogDebug(string.Format("Adding data in the SQL snapshot database took {0}.", end - start), "SqlSnapshotStore\\Add");
130 : }
131 : catch (Exception exception)
132 : {
133 : Logger.LogError("There was an issue persisting data to the SQL snapshot database.", exception: exception);
134 : throw;
135 : }
136 : finally
137 : {
138 : Logger.LogDebug("Adding data to the SQL snapshot database... Done", "SqlSnapshotStore\\Add");
139 : }
140 : }
141 : }
142 : }
|