/// <summary> /// Persist the provided <paramref name="eventData"/> into SQL Server. /// </summary> /// <param name="eventData">The <see cref="EventData"/> to persist.</param> protected override void PersistEvent(EventData eventData) { using (SqlEventStoreDataContext dbDataContext = CreateDbDataContext(eventData.AggregateId.Substring(0, eventData.AggregateId.IndexOf("/", StringComparison.InvariantCultureIgnoreCase)))) { Add(dbDataContext, eventData); } }
/// <summary> /// Get all <see cref="IEvent{TAuthenticationToken}"/> instances for the given <paramref name="correlationId"/>. /// </summary> /// <param name="correlationId">The <see cref="IMessage.CorrelationId"/> of the <see cref="IEvent{TAuthenticationToken}"/> instances to retrieve.</param> public override IEnumerable <EventData> Get(Guid correlationId) { using (SqlEventStoreDataContext dbDataContext = CreateDbDataContext()) { string commandTimeoutValue; int commandTimeout; bool found = ConfigurationManager.TryGetSetting(SqlEventStoreGetByCorrelationIdCommandTimeout, out commandTimeoutValue); if (found && int.TryParse(commandTimeoutValue, out commandTimeout)) { #if NET40 // Get the ObjectContext related to this DbContext var objectContext = (dbDataContext as IObjectContextAdapter).ObjectContext; // Sets the command timeout for all the commands objectContext.CommandTimeout = commandTimeout; #else dbDataContext.Database.SetCommandTimeout(commandTimeout); #endif } IEnumerable <EventData> query = GetEventStoreTable(dbDataContext) .AsQueryable() .Where(eventData => eventData.CorrelationId == correlationId) .OrderBy(eventData => eventData.Timestamp); return(query.ToList()); } }
/// <summary> /// Persist the provided <paramref name="eventData"/> into each SQL Server in <see cref="WritableConnectionStrings"/>. /// A single <see cref="TransactionScope"/> wraps all SQL servers, so all must complete successfully, or they will ALL roll back. /// </summary> /// <param name="eventData">The <see cref="EventData"/> to persist.</param> protected override void PersistEvent(EventData eventData) { try { using (TransactionScope scope = new TransactionScope()) { IList <Task> persistTasks = new List <Task>(); foreach (string connectionString in WritableConnectionStrings) { // Do not remove this variable copying or the parallel task stuff will bork. var safeConnectionString = connectionString; DependentTransaction subTransaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete); Task task = Task.Factory.StartNewSafely ( (subTransactionObject) => { var subTrx = (DependentTransaction)subTransactionObject; //Pass the DependentTransaction to the scope, so that work done in the scope becomes part of the transaction passed to the worker thread using (TransactionScope ts = new TransactionScope(subTrx)) { using (SqlEventStoreDataContext dbDataContext = new SqlEventStoreDataContext(safeConnectionString)) Add(dbDataContext, eventData); //Call complete on the transaction scope ts.Complete(); } //Call complete on the dependent transaction subTrx.Complete(); }, subTransaction ); persistTasks.Add(task); } bool anyFailed = Task.Factory.ContinueWhenAll(persistTasks.ToArray(), tasks => { return(tasks.Any(task => task.IsFaulted)); }).Result; if (anyFailed) { throw new AggregateException("Persisting data to the SQL event store failed. Check the logs for more details."); } scope.Complete(); } } catch (TransactionException exception) { Logger.LogError("There was an issue with the SQL transaction persisting data to the SQL event store.", exception: exception); throw; } catch (Exception exception) { Logger.LogError("There was an issue persisting data to the SQL event store.", exception: exception); throw; } }
/// <summary> /// 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"/> up to and including the provided <paramref name="version"/>. /// </summary> /// <param name="aggregateRootType"> <see cref="Type"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/> the <see cref="IEvent{TAuthenticationToken}"/> was raised in.</param> /// <param name="aggregateId">The <see cref="IAggregateRoot{TAuthenticationToken}.Id"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/>.</param> /// <param name="version">Load events up-to and including from this version</param> public override IEnumerable <IEvent <TAuthenticationToken> > GetToVersion(Type aggregateRootType, Guid aggregateId, int version) { string streamName = string.Format(CqrsEventStoreStreamNamePattern, aggregateRootType.FullName, aggregateId); using (SqlEventStoreDataContext dbDataContext = CreateDbDataContext(aggregateRootType.FullName)) { IEnumerable <EventData> query = GetEventStoreTable(dbDataContext) .AsQueryable() .Where(eventData => eventData.AggregateId == streamName && eventData.Version <= version) .OrderByDescending(eventData => eventData.Version); return(query .Select(EventDeserialiser.Deserialise) .ToList()); } }
/// <summary> /// Creates a new <see cref="DataContext"/> using connection string settings from <see cref="ConfigurationManager"/>. /// </summary> protected virtual DataContext CreateDbDataContext(string aggregateRootTypeName = null) { string connectionStringKey; string applicationKey; if (!ConfigurationManager.TryGetSetting(SqlEventStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey)) { if (!ConfigurationManager.TryGetSetting(SqlEventStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey)) { if (!ConfigurationManager.TryGetSetting(SqlDataStore <Entity> .SqlDataStoreDbFileOrServerOrConnectionApplicationKey, out connectionStringKey) || string.IsNullOrEmpty(connectionStringKey)) { throw new MissingApplicationSettingForConnectionStringException(SqlEventStoreConnectionNameApplicationKey); } } } else { try { connectionStringKey = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey].ConnectionString; } catch (NullReferenceException exception) { throw new MissingConnectionStringException(applicationKey, exception); } } string tableName; if (!string.IsNullOrWhiteSpace(aggregateRootTypeName) && ConfigurationManager.TryGetSetting(string.Format(SqlEventStoreTableNameApplicationKeyPattern, aggregateRootTypeName), out tableName) && !string.IsNullOrEmpty(tableName)) { bool autoname; if (bool.TryParse(tableName, out autoname)) { if (autoname) { return(SqlEventStoreDataContext.New <EventData>(aggregateRootTypeName.Replace(".", "_"), connectionStringKey)); } } else { return(SqlEventStoreDataContext.New <EventData>(tableName, connectionStringKey)); } } return(new SqlEventStoreDataContext(connectionStringKey)); }
/// <summary> /// Get the latest <see cref="Snapshot"/> from storage. /// </summary> /// <returns>The most recent <see cref="Snapshot"/> of</returns> protected override Snapshot Get(Type aggregateRootType, string streamName) { using (SqlEventStoreDataContext dbDataContext = CreateDbDataContext(aggregateRootType.FullName)) { EventData query = GetEventStoreSnapshotTable(dbDataContext) .AsQueryable() .Where(snapshot => snapshot.AggregateId == streamName) .OrderByDescending(eventData => eventData.Version) .Take(1) .SingleOrDefault(); if (query == null) { return(null); } return(EventDeserialiser.Deserialise(query)); } }
/// <summary> /// Persist the provided <paramref name="data"/> into SQL Server using the provided <paramref name="dbDataContext"/>. /// </summary> protected virtual void Add(SqlEventStoreDataContext dbDataContext, EventData data) { Logger.LogDebug("Adding data to the SQL eventstore database", "SqlEventStore\\Add"); try { DateTime start = DateTime.Now; GetEventStoreTable(dbDataContext).Add(data); dbDataContext.SaveChanges(); DateTime end = DateTime.Now; Logger.LogDebug(string.Format("Adding data in the SQL eventstore database took {0}.", end - start), "SqlEventStore\\Add"); } catch (Exception exception) { Logger.LogError("There was an issue persisting data to the SQL event store.", exception: exception); throw; } finally { Logger.LogDebug("Adding data to the SQL eventstore database... Done", "SqlEventStore\\Add"); } }
/// <summary> /// Creates a new <see cref="DataContext"/> using connection string settings from ConfigurationManager. /// </summary> protected virtual DataContext CreateDbDataContext(string aggregateRootTypeName = null) { string connectionStringKey; string applicationKey; if (!ConfigurationManager.TryGetSetting(SqlSnapshotStoreConnectionNameApplicationKey, out applicationKey) || string.IsNullOrEmpty(applicationKey)) { throw new MissingApplicationSettingForConnectionStringException(SqlSnapshotStoreConnectionNameApplicationKey); } ConnectionStringSettings connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[applicationKey]; if (connectionString == null) { throw new MissingConnectionStringException(applicationKey); } connectionStringKey = connectionString.ConnectionString; string tableName; if (!string.IsNullOrWhiteSpace(aggregateRootTypeName) && ConfigurationManager.TryGetSetting(string.Format(SqlSnapshotStoreTableNameApplicationKeyPattern, aggregateRootTypeName), out tableName) && !string.IsNullOrEmpty(tableName)) { bool autoname; if (bool.TryParse(tableName, out autoname)) { if (autoname) { return(SqlEventStoreDataContext.New <EventData>(aggregateRootTypeName.Replace(".", "_"), connectionStringKey)); } } else { return(SqlEventStoreDataContext.New <EventData>(tableName, connectionStringKey)); } } return(SqlEventStoreDataContext.New <EventData>("Snapshots", connectionStringKey)); }
/// <summary> /// Creates a new <see cref="DbContext"/> using connection string settings from <see cref="ConfigurationManager"/>. /// </summary> protected virtual SqlEventStoreDataContext CreateDbDataContext(string aggregateRootTypeName = null) { string connectionStringKey = ConfigurationManager.GetConnectionStringBySettingKey(SqlEventStoreConnectionNameApplicationKey, true, true); string tableName; if (!string.IsNullOrWhiteSpace(aggregateRootTypeName) && ConfigurationManager.TryGetSetting(string.Format(SqlEventStoreTableNameApplicationKeyPattern, aggregateRootTypeName), out tableName) && !string.IsNullOrEmpty(tableName)) { bool autoname; if (bool.TryParse(tableName, out autoname)) { if (autoname) { return(SqlEventStoreDataContext.New(aggregateRootTypeName.Replace(".", "_"), connectionStringKey)); } } else { return(SqlEventStoreDataContext.New(tableName, connectionStringKey)); } } return(new SqlEventStoreDataContext(connectionStringKey)); }
/// <summary> /// Saves the provided <paramref name="snapshot"/> into storage. /// </summary> /// <param name="snapshot">the <see cref="Snapshot"/> to save and store.</param> public override void Save(Snapshot snapshot) { using (SqlEventStoreDataContext dbDataContext = CreateDbDataContext(snapshot.GetType().Name)) Add(dbDataContext, snapshot); }
/// <summary> /// Gets the <see cref="DbSet{TEntity}"/> of <see cref="Snapshot"/>. /// </summary> /// <param name="dbDataContext">The <see cref="DbContext"/> to use.</param> protected virtual DbSet <EventData> GetEventStoreSnapshotTable(SqlEventStoreDataContext dbDataContext) { // Get a typed table to run queries. return(dbDataContext.Set <EventData>()); }