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.Data.Linq;
12 : using System.Linq;
13 : using cdmdotnet.Logging;
14 : using Cqrs.Configuration;
15 : using Cqrs.DataStores;
16 : using Cqrs.Domain;
17 : using Cqrs.Entities;
18 : using Cqrs.Exceptions;
19 : using Cqrs.Messages;
20 :
21 : namespace Cqrs.Events
22 : {
23 : /// <summary>
24 : /// A simplified SqlServer based <see cref="EventStore{TAuthenticationToken}"/> that uses LinqToSql and follows a rigid schema.
25 : /// </summary>
26 : /// <typeparam name="TAuthenticationToken">The <see cref="Type"/> of the authentication token.</typeparam>
27 : public class SqlEventStore<TAuthenticationToken> : EventStore<TAuthenticationToken>
28 1 : {
29 : internal const string SqlEventStoreDbFileOrServerOrConnectionApplicationKey = @"SqlEventStoreDbFileOrServerOrConnection";
30 :
31 : internal const string SqlEventStoreConnectionNameApplicationKey = @"Cqrs.SqlEventStore.ConnectionStringName";
32 :
33 : internal const string OldSqlEventStoreGetByCorrelationIdCommandTimeout = @"SqlEventStoreGetByCorrelationIdCommandTimeout";
34 :
35 : internal const string SqlEventStoreGetByCorrelationIdCommandTimeout = @"Cqrs.SqlEventStore.GetByCorrelationId.CommandTimeout";
36 :
37 : /// <summary>
38 : /// Gets or sets the <see cref="IConfigurationManager"/>.
39 : /// </summary>
40 : protected IConfigurationManager ConfigurationManager { get; private set; }
41 :
42 : /// <summary>
43 : /// Instantiate a new instance of the <see cref="SqlEventStore{TAuthenticationToken}"/> class.
44 : /// </summary>
45 1 : public SqlEventStore(IEventBuilder<TAuthenticationToken> eventBuilder, IEventDeserialiser<TAuthenticationToken> eventDeserialiser, ILogger logger, IConfigurationManager configurationManager)
46 : : base(eventBuilder, eventDeserialiser, logger)
47 : {
48 : ConfigurationManager = configurationManager;
49 : }
50 :
51 : #region Overrides of EventStore<TAuthenticationToken>
52 :
53 : /// <summary>
54 : /// Gets a collection of <see cref="IEvent{TAuthenticationToken}"/> for the <see cref="IAggregateRoot{TAuthenticationToken}"/> of type <paramref name="aggregateRootType"/> with the ID matching the provided <paramref name="aggregateId"/>.
55 : /// </summary>
56 : /// <param name="aggregateRootType"> <see cref="Type"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/> the <see cref="IEvent{TAuthenticationToken}"/> was raised in.</param>
57 : /// <param name="aggregateId">The <see cref="IAggregateRoot{TAuthenticationToken}.Id"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/>.</param>
58 : /// <param name="useLastEventOnly">Loads only the last event<see cref="IEvent{TAuthenticationToken}"/>.</param>
59 : /// <param name="fromVersion">Load events starting from this version</param>
60 1 : public override IEnumerable<IEvent<TAuthenticationToken>> Get(Type aggregateRootType, Guid aggregateId, bool useLastEventOnly = false, int fromVersion = -1)
61 : {
62 : string streamName = string.Format(CqrsEventStoreStreamNamePattern, aggregateRootType.FullName, aggregateId);
63 :
64 : using (DataContext dbDataContext = CreateDbDataContext())
65 : {
66 : IEnumerable<EventData> query = GetEventStoreTable(dbDataContext)
67 : .AsQueryable()
68 : .Where(eventData => eventData.AggregateId == streamName && eventData.Version > fromVersion)
69 : .OrderByDescending(eventData => eventData.Version);
70 :
71 : if (useLastEventOnly)
72 : query = query.AsQueryable().Take(1);
73 :
74 : return query
75 : .Select(EventDeserialiser.Deserialise)
76 : .ToList();
77 : }
78 : }
79 :
80 : /// <summary>
81 : /// Get all <see cref="IEvent{TAuthenticationToken}"/> instances for the given <paramref name="correlationId"/>.
82 : /// </summary>
83 : /// <param name="correlationId">The <see cref="IMessage.CorrelationId"/> of the <see cref="IEvent{TAuthenticationToken}"/> instances to retrieve.</param>
84 1 : public override IEnumerable<EventData> Get(Guid correlationId)
85 : {
86 : using (DataContext dbDataContext = CreateDbDataContext())
87 : {
88 : string commandTimeoutValue;
89 : int commandTimeout;
90 : bool found = ConfigurationManager.TryGetSetting(SqlEventStoreGetByCorrelationIdCommandTimeout, out commandTimeoutValue);
91 : if (!found)
92 : found = ConfigurationManager.TryGetSetting(OldSqlEventStoreGetByCorrelationIdCommandTimeout, out commandTimeoutValue);
93 : if (found && int.TryParse(commandTimeoutValue, out commandTimeout))
94 : dbDataContext.CommandTimeout = commandTimeout;
95 :
96 : IEnumerable<EventData> query = GetEventStoreTable(dbDataContext)
97 : .AsQueryable()
98 : .Where(eventData => eventData.CorrelationId == correlationId)
99 : .OrderBy(eventData => eventData.Timestamp);
100 :
101 : return query.ToList();
102 : }
103 : }
104 :
105 : /// <summary>
106 : /// Persist the provided <paramref name="eventData"/> into SQL Server.
107 : /// </summary>
108 : /// <param name="eventData">The <see cref="EventData"/> to persist.</param>
109 1 : protected override void PersistEvent(EventData eventData)
110 : {
111 : using (DataContext dbDataContext = CreateDbDataContext())
112 : {
113 : Add(dbDataContext, eventData);
114 : }
115 : }
116 :
117 : #endregion
118 :
119 : /// <summary>
120 : /// Creates a new <see cref="DataContext"/> using connection string settings from <see cref="ConfigurationManager"/>.
121 : /// </summary>
122 1 : protected virtual DataContext CreateDbDataContext()
123 : {
124 : string connectionStringKey;
125 : string applicationKey;
126 : if (!ConfigurationManager.TryGetSetting(SqlEventStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey))
127 : {
128 : if (!ConfigurationManager.TryGetSetting(SqlEventStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey))
129 : {
130 : if (!ConfigurationManager.TryGetSetting(SqlDataStore<Entity>.SqlDataStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey))
131 : {
132 : throw new MissingApplicationSettingForConnectionStringException(SqlEventStoreConnectionNameApplicationKey);
133 : }
134 : }
135 : }
136 : else
137 : {
138 : try
139 : {
140 : connectionStringKey = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey].ConnectionString;
141 : }
142 : catch (NullReferenceException exception)
143 : {
144 : throw new MissingConnectionStringException(applicationKey, exception);
145 : }
146 : }
147 : return new DataContext(connectionStringKey);
148 : }
149 :
150 : /// <summary>
151 : /// Gets the <see cref="Table{TEntity}"/> of <see cref="EventData"/>.
152 : /// </summary>
153 : /// <param name="dbDataContext">The <see cref="DataContext"/> to use.</param>
154 1 : protected virtual Table<EventData> GetEventStoreTable(DataContext dbDataContext)
155 : {
156 : // Get a typed table to run queries.
157 : return dbDataContext.GetTable<EventData>();
158 : }
159 :
160 : /// <summary>
161 : /// Persist the provided <paramref name="data"/> into SQL Server using the provided <paramref name="dbDataContext"/>.
162 : /// </summary>
163 1 : protected virtual void Add(DataContext dbDataContext, EventData data)
164 : {
165 : Logger.LogDebug("Adding data to the SQL eventstore database", "SqlEventStore\\Add");
166 : try
167 : {
168 : DateTime start = DateTime.Now;
169 : GetEventStoreTable(dbDataContext).InsertOnSubmit(data);
170 : dbDataContext.SubmitChanges();
171 : DateTime end = DateTime.Now;
172 : Logger.LogDebug(string.Format("Adding data in the SQL eventstore database took {0}.", end - start), "SqlEventStore\\Add");
173 : }
174 : catch (Exception exception)
175 : {
176 : Logger.LogError("There was an issue persisting data to the SQL event store.", exception: exception);
177 : throw;
178 : }
179 : finally
180 : {
181 : Logger.LogDebug("Adding data to the SQL eventstore database... Done", "SqlEventStore\\Add");
182 : }
183 : }
184 : }
185 : }
|