/// <summary> /// Asynchronously commits all changes made within the scope of the current <see cref="DataAccessTransaction" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation. /// </returns> protected abstract Task CommitAsync(ConcurrencyControlToken controlToken);
/// <summary> /// Asynchronously initiates the current <see cref="DataAccessTransaction" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation. /// </returns> protected abstract Task BeginAsync(ConcurrencyControlToken controlToken);
/// <summary> /// Commits all changes made within the scope of the current <see cref="DataAccessTransaction" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void Commit(ConcurrencyControlToken controlToken);
/// <summary> /// Determines whether or not the specified entity exists in the current <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="entity"> /// The entity to evaluate. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// <see langword="true" /> if the specified entity exists in the current <see cref="DataAccessRepository{TEntity}" />, /// otherwise <see langword="false" />. /// </returns> protected abstract Boolean Contains(TEntity entity, ConcurrencyControlToken controlToken);
/// <summary> /// Asynchronously publishes the specified request message to a bus and waits for the correlated response message. /// </summary> /// <typeparam name="TRequestMessage"> /// The type of the request message. /// </typeparam> /// <typeparam name="TResponseMessage"> /// The type of the response message. /// </typeparam> /// <param name="requestMessage"> /// The request message to publish. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation and containing the correlated response message. /// </returns> protected sealed override async Task <TResponseMessage> RequestAsync <TRequestMessage, TResponseMessage>(TRequestMessage requestMessage, ConcurrencyControlToken controlToken) { var requestMessageType = typeof(TRequestMessage); var serializedRequestMessage = SerializeMessage(requestMessage); var azureServiceBusRequestMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedRequestMessage, requestMessageType, MessageSerializationFormat, requestMessage.Identifier, requestMessage.CorrelationIdentifier); var requestClient = ClientManager.GetRequestClient <TRequestMessage, TResponseMessage>(); ClientManager.AddOutstandingRequest(requestMessage.Identifier); using (var sendTask = requestClient.SendAsync(azureServiceBusRequestMessage)) { using (var waitForResponseTask = ClientManager.WaitForResponseAsync(requestMessage.Identifier)) { await Task.WhenAll(sendTask, waitForResponseTask); return(DeserializeMessage <TResponseMessage>(waitForResponseTask.Result.Body)); } } }
/// <summary> /// Adds the specified entity to the current <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="entity"> /// The entity to add. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void Add(TEntity entity, ConcurrencyControlToken controlToken);
/// <summary> /// Returns all entities from the current <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// All entities within the current <see cref="DataAccessRepository{TEntity}" />. /// </returns> protected abstract IQueryable <TEntity> All(ConcurrencyControlToken controlToken);
/// <summary> /// Processes the specified command. /// </summary> /// <param name="command"> /// The command to process. /// </param> /// <param name="mediator"> /// A processing intermediary that is used to process sub-commands. Do not process <paramref name="command" /> using /// <paramref name="mediator" />, as doing so will generally result in infinite-looping. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// The result that is emitted when processing the command. /// </returns> protected override TResponseMessage Process(TRequestMessage command, ICommandMediator mediator, ConcurrencyControlToken controlToken) => base.Process(command, mediator, controlToken);
/// <summary> /// Processes the specified command. /// </summary> /// <param name="command"> /// The command to process. /// </param> /// <param name="mediator"> /// A processing intermediary that is used to process sub-commands. Do not process <paramref name="command" /> using /// <paramref name="mediator" />, as doing so will generally result in infinite-looping. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected override void Process(TMessage command, ICommandMediator mediator, ConcurrencyControlToken controlToken) => base.Process(command, mediator, controlToken);
/// <summary> /// Registers the specified message handler with the bus. /// </summary> /// <typeparam name="TRequestMessage"> /// The type of the request message. /// </typeparam> /// <typeparam name="TResponseMessage"> /// The type of the response message. /// </typeparam> /// <param name="requestMessageHandler"> /// A function that handles a request message. /// </param> /// <param name="requestMessageEntityType"> /// The entity type that is used for publishing and subscribing to request messages. /// </param> /// <param name="responseMessageEntityType"> /// The entity type that is used for publishing and subscribing to response messages. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void RegisterHandler <TRequestMessage, TResponseMessage>(Func <TRequestMessage, TResponseMessage> requestMessageHandler, MessagingEntityType requestMessageEntityType, MessagingEntityType responseMessageEntityType, ConcurrencyControlToken controlToken) where TRequestMessage : class, IRequestMessage <TResponseMessage> where TResponseMessage : class, IResponseMessage;
/// <summary> /// Processes the specified command. /// </summary> /// <param name="command"> /// The command to process. /// </param> /// <param name="mediator"> /// A processing intermediary that is used to process sub-commands. Do not process <paramref name="command" /> using /// <paramref name="mediator" />, as doing so will generally result in infinite-looping. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected sealed override void Process(SimulatedCommand command, ICommandMediator mediator, ConcurrencyControlToken controlToken) => command.IsProcessed = true;
/// <summary> /// Registers the specified message handler with the bus. /// </summary> /// <typeparam name="TMessage"> /// The type of the message. /// </typeparam> /// <param name="messageHandler"> /// An action that handles a message. /// </param> /// <param name="entityType"> /// The targeted entity type. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void RegisterHandler <TMessage>(Action <TMessage> messageHandler, MessagingEntityType entityType, ConcurrencyControlToken controlToken) where TMessage : class, IMessage;
/// <summary> /// Registers the specified message handler with the bus. /// </summary> /// <typeparam name="TRequestMessage"> /// The type of the request message. /// </typeparam> /// <typeparam name="TResponseMessage"> /// The type of the response message. /// </typeparam> /// <param name="requestMessageHandler"> /// A function that handles a request message. /// </param> /// <param name="requestMessageEntityType"> /// The entity type that is used for publishing and subscribing to request messages. /// </param> /// <param name="responseMessageEntityType"> /// The entity type that is used for publishing and subscribing to response messages. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected sealed override void RegisterHandler <TRequestMessage, TResponseMessage>(Func <TRequestMessage, TResponseMessage> requestMessageHandler, MessagingEntityType requestMessageEntityType, MessagingEntityType responseMessageEntityType, ConcurrencyControlToken controlToken) { var responseMessageType = typeof(TResponseMessage); var requestReceiveClient = ClientManager.GetReceiveClient <TRequestMessage>(requestMessageEntityType); var responseSendClient = ClientManager.GetSendClient <TResponseMessage>(responseMessageEntityType); var messageHandlerFunction = new Func <AzureServiceBusMessage, CancellationToken, Task>(async(azureServiceBusRequestMessage, cancellationToken) => { var lockToken = azureServiceBusRequestMessage.SystemProperties?.LockToken; if (lockToken is null) { throw new MessageSubscriptionException("The lock token is invalid."); } var deserializedRequestMessage = DeserializeMessage <TRequestMessage>(azureServiceBusRequestMessage.Body); var requestMessageIdentifier = deserializedRequestMessage.Identifier.ToSerializedString(); if (requestReceiveClient.IsClosedOrClosing) { throw new MessageSubscriptionException("The message cannot be processed because the receive client is unavailable."); } try { cancellationToken.ThrowIfCancellationRequested(); var responseMessage = requestMessageHandler(deserializedRequestMessage); var serializedResponseMessage = SerializeMessage(responseMessage); var azureServiceBusResponseMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedResponseMessage, responseMessageType, MessageSerializationFormat, responseMessage.Identifier, responseMessage.CorrelationIdentifier, deserializedRequestMessage.Identifier); await responseSendClient.SendAsync(azureServiceBusResponseMessage).ContinueWith(async(task) => { switch (task.Status) { case TaskStatus.RanToCompletion: await requestReceiveClient.CompleteAsync(lockToken).ConfigureAwait(false); break; default: await requestReceiveClient.AbandonAsync(lockToken).ConfigureAwait(false); break; } }).ConfigureAwait(false); } catch { await requestReceiveClient.AbandonAsync(lockToken).ConfigureAwait(false); throw; } }); requestReceiveClient.RegisterMessageHandler(messageHandlerFunction, ClientManager.ReceiverOptionsForRequestMessages); }
/// <summary> /// Registers the specified message handler with the bus. /// </summary> /// <typeparam name="TMessage"> /// The type of the message. /// </typeparam> /// <param name="messageHandler"> /// An action that handles a message. /// </param> /// <param name="entityType"> /// The targeted entity type. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected sealed override void RegisterHandler <TMessage>(Action <TMessage> messageHandler, MessagingEntityType entityType, ConcurrencyControlToken controlToken) { var receiveClient = ClientManager.GetReceiveClient <TMessage>(entityType); var messageHandlerFunction = new Func <AzureServiceBusMessage, CancellationToken, Task>(async(azureServiceBusMessage, cancellationToken) => { var lockToken = azureServiceBusMessage.SystemProperties?.LockToken; if (lockToken is null) { throw new MessageSubscriptionException("The message cannot be processed because the lock token is invalid."); } var deserializedMessage = DeserializeMessage <TMessage>(azureServiceBusMessage.Body); if (receiveClient.IsClosedOrClosing) { throw new MessageSubscriptionException("The message cannot be processed because the receive client is unavailable."); } try { cancellationToken.ThrowIfCancellationRequested(); messageHandler(deserializedMessage); await receiveClient.CompleteAsync(lockToken).ConfigureAwait(false); } catch { await receiveClient.AbandonAsync(lockToken).ConfigureAwait(false); throw; } }); receiveClient.RegisterMessageHandler(messageHandlerFunction, ClientManager.ReceiverOptionsForGeneralMessages); }
/// <summary> /// Rejects all changes made within the scope of the current <see cref="DataAccessTransaction" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void Reject(ConcurrencyControlToken controlToken);
/// <summary> /// Processes the specified command. /// </summary> /// <param name="command"> /// The command to process. /// </param> /// <param name="repositories"> /// An object that provides access to data access repositories. /// </param> /// <param name="transaction"> /// A transaction that is used to process the command. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected override void Process(AddFibonacciNumberCommand command, IFactoryProducedInstanceGroup repositories, IDataAccessTransaction transaction, ConcurrencyControlToken controlToken) { var fibonacciNumberSeries = NumberSeries.Named.Fibonacci; var numberRepository = repositories.Get <NumberRepository>(); var number = numberRepository.FindByValue(command.NumberValue); if (number is null) { number = new Number() { Identifier = Guid.NewGuid(), Value = command.NumberValue }; numberRepository.Add(number); } var numberSeriesNumberRespository = repositories.Get <NumberSeriesNumberRepository>(); var numberSeriesNumber = numberSeriesNumberRespository.FindWhere(entity => entity.Number.Value == number.Value && entity.NumberSeriesIdentifier == fibonacciNumberSeries.Identifier).SingleOrDefault(); if (numberSeriesNumber is null) { numberSeriesNumber = new NumberSeriesNumber() { Identifier = Guid.NewGuid(), Number = number, NumberIdentifier = number.Identifier, NumberSeriesIdentifier = fibonacciNumberSeries.Identifier }; numberSeriesNumberRespository.Add(numberSeriesNumber); } }
/// <summary> /// Asynchronously all changes made within the scope of the current <see cref="DataAccessTransaction" />. /// </summary> /// <returns> /// A task representing the asynchronous operation. /// </returns> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract Task RejectAsync(ConcurrencyControlToken controlToken);
/// <summary> /// Asynchronously publishes the specified message to a bus. /// </summary> /// <typeparam name="TMessage"> /// The type of the message to publish. /// </typeparam> /// <param name="message"> /// The message to publish. /// </param> /// <param name="entityType"> /// The targeted entity type. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation. /// </returns> protected abstract Task PublishAsync <TMessage>(TMessage message, MessagingEntityType entityType, ConcurrencyControlToken controlToken) where TMessage : class, IMessage;
/// <summary> /// Adds the specified entities to the current <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="entities"> /// The entities to add. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void AddRange(IEnumerable <TEntity> entities, ConcurrencyControlToken controlToken);
/// <summary> /// Asynchronously publishes the specified request message to a bus and waits for the correlated response message. /// </summary> /// <typeparam name="TRequestMessage"> /// The type of the request message. /// </typeparam> /// <typeparam name="TResponseMessage"> /// The type of the response message. /// </typeparam> /// <param name="requestMessage"> /// The request message to publish. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation and containing the correlated response message. /// </returns> protected abstract Task <TResponseMessage> RequestAsync <TRequestMessage, TResponseMessage>(TRequestMessage requestMessage, ConcurrencyControlToken controlToken) where TRequestMessage : class, IRequestMessage <TResponseMessage> where TResponseMessage : class, IResponseMessage;
/// <summary> /// Determines whether or not any entities matching the specified predicate exist in the current /// <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="predicate"> /// An expression to test each entity for a condition. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// <see langword="true" /> if any entities matching the specified predicate exist in the current /// <see cref="DataAccessRepository{TEntity}" />, otherwise <see langword="false" />. /// </returns> protected abstract Boolean AnyWhere(Expression <Func <TEntity, Boolean> > predicate, ConcurrencyControlToken controlToken);
/// <summary> /// Initiates the current <see cref="DataAccessTransaction" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> protected abstract void Begin(ConcurrencyControlToken controlToken);
/// <summary> /// Returns the number of entities in the current <see cref="DataAccessRepository{TEntity}" />. /// </summary> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// The number of entities in the current <see cref="DataAccessRepository{TEntity}" />. /// </returns> protected abstract Int64 Count(ConcurrencyControlToken controlToken);
/// <summary> /// Asynchronously publishes the specified message to a bus. /// </summary> /// <typeparam name="TMessage"> /// The type of the message to publish. /// </typeparam> /// <param name="message"> /// The message to publish. /// </param> /// <param name="entityType"> /// The targeted entity type. /// </param> /// <param name="controlToken"> /// A token that ensures thread safety for the operation. /// </param> /// <returns> /// A task representing the asynchronous operation. /// </returns> protected sealed override async Task PublishAsync <TMessage>(TMessage message, MessagingEntityType entityType, ConcurrencyControlToken controlToken) { var messageType = typeof(TMessage); var serializedMessage = SerializeMessage(message); var azureServiceBusMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedMessage, messageType, MessageSerializationFormat, message.Identifier, message.CorrelationIdentifier); var sendClient = ClientManager.GetSendClient <TMessage>(entityType); await sendClient.SendAsync(azureServiceBusMessage).ConfigureAwait(false); }