diff --git a/Source/Euonia.Bus.Abstract/Exceptions/MessageDeliverException.cs b/Source/Euonia.Bus.Abstract/Exceptions/MessageDeliverException.cs new file mode 100644 index 0000000..e3d3707 --- /dev/null +++ b/Source/Euonia.Bus.Abstract/Exceptions/MessageDeliverException.cs @@ -0,0 +1,34 @@ +namespace Nerosoft.Euonia.Bus; + +/// +/// Represents errors that occur during message deliver. +/// +public class MessageDeliverException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public MessageDeliverException() + : base("Error occurred during message deliver.") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + public MessageDeliverException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public MessageDeliverException(string message, Exception innerException) + : base(message, innerException) + { + } +} diff --git a/Source/Euonia.Bus.Abstract/Exceptions/MessageTypeException.cs b/Source/Euonia.Bus.Abstract/Exceptions/MessageTypeException.cs new file mode 100644 index 0000000..0270b44 --- /dev/null +++ b/Source/Euonia.Bus.Abstract/Exceptions/MessageTypeException.cs @@ -0,0 +1,34 @@ +namespace Nerosoft.Euonia.Bus; + +/// +/// Represents errors that occur when the message type is invalid. +/// +public class MessageTypeException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public MessageTypeException() + : base("The message type is invalid.") + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + public MessageTypeException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public MessageTypeException(string message, Exception innerException) + : base(message, innerException) + { + } +} diff --git a/Source/Euonia.Bus.InMemory/InMemoryRecipientRegistrar.cs b/Source/Euonia.Bus.InMemory/InMemoryRecipientRegistrar.cs index c77b1bf..049f058 100644 --- a/Source/Euonia.Bus.InMemory/InMemoryRecipientRegistrar.cs +++ b/Source/Euonia.Bus.InMemory/InMemoryRecipientRegistrar.cs @@ -46,7 +46,7 @@ public async Task RegisterAsync(IEnumerable registrations, } else { - throw new InvalidOperationException(); + throw new MessageTypeException("The message type is neither a queue nor a topic."); } } diff --git a/Source/Euonia.Bus.InMemory/Messenger/StrongReferenceMessenger.cs b/Source/Euonia.Bus.InMemory/Messenger/StrongReferenceMessenger.cs index c3e7ddd..52679d5 100644 --- a/Source/Euonia.Bus.InMemory/Messenger/StrongReferenceMessenger.cs +++ b/Source/Euonia.Bus.InMemory/Messenger/StrongReferenceMessenger.cs @@ -606,7 +606,7 @@ public TMessage Send(TMessage message, TToken token) /// The token indicating what channel to use. /// The message that was sent (ie. ). /// Thrown if or are . - public TMessage UnsafeSend(TMessage message, TToken token) + internal TMessage UnsafeSend(TMessage message, TToken token) where TMessage : class where TToken : IEquatable { @@ -624,7 +624,7 @@ public TMessage UnsafeSend(TMessage message, TToken token) // Check whether there are any registered recipients if (!TryGetMapping(out var mapping)) { - throw new InvalidOperationException("No recipients registered for the input message type."); + throw new MessageDeliverException("No recipients registered for the input message type."); } // Check the number of remaining handlers, see below @@ -632,7 +632,7 @@ public TMessage UnsafeSend(TMessage message, TToken token) if (totalHandlersCount == 0) { - throw new InvalidOperationException("No recipients registered for the input message type."); + throw new MessageDeliverException("No recipients registered for the input message type."); } pairs = rentedArray = ArrayPool.Shared.Rent(2 * totalHandlersCount); @@ -670,7 +670,7 @@ public TMessage UnsafeSend(TMessage message, TToken token) if (totalHandlersCount == 0) { - throw new InvalidOperationException("No recipients registered for the input message type."); + throw new MessageDeliverException("No recipients registered for the input message type."); } // Rent the array and also assign it to a span, which will be used to access values. @@ -704,7 +704,7 @@ public TMessage UnsafeSend(TMessage message, TToken token) if (i == 0) { - throw new InvalidOperationException("No recipients registered for the input message type and token."); + throw new MessageDeliverException("No recipients registered for the input message type and token."); } } } diff --git a/Source/Euonia.Bus.RabbitMq/RabbitMqDispatcher.cs b/Source/Euonia.Bus.RabbitMq/RabbitMqDispatcher.cs index 8496189..2f80372 100644 --- a/Source/Euonia.Bus.RabbitMq/RabbitMqDispatcher.cs +++ b/Source/Euonia.Bus.RabbitMq/RabbitMqDispatcher.cs @@ -1,9 +1,11 @@ -using Microsoft.Extensions.Logging; +using System.Net.Sockets; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Polly; using RabbitMQ.Client; using RabbitMQ.Client.Events; +using RabbitMQ.Client.Exceptions; namespace Nerosoft.Euonia.Bus.RabbitMq; @@ -45,7 +47,8 @@ public async Task PublishAsync(RoutedMessage message, Cancel props.Headers[Constants.MessageHeaders.MessageType] = typeName; props.Type = typeName; - await Policy.Handle() + await Policy.Handle() + .Or() .WaitAndRetryAsync(_options.MaxFailureRetries, _ => TimeSpan.FromSeconds(3), (exception, _, retryCount, _) => { _logger.LogError(exception, "Retry:{RetryCount}, {Message}", retryCount, exception.Message); @@ -66,21 +69,9 @@ public async Task SendAsync(RoutedMessage message, Cancellat { using var channel = _connection.CreateChannel(); - { - var queueDeclare = channel.DeclareQueuePassively($"{_options.QueueName}${message.Channel}$"); + var requestQueueName = $"{_options.QueueName}${message.Channel}$"; - if (queueDeclare == null) - { - throw new InvalidOperationException("Channel not found in vhost '/'."); - //channel.QueueDeclare($"{_options.QueueName}${message.Channel}$", true, false, false, null); - //channel.BasicQos(0, 1, false); - } - - if (queueDeclare.ConsumerCount < 1) - { - throw new InvalidOperationException("No consumer found for the channel."); - } - } + CheckQueue(channel, requestQueueName); var typeName = message.GetTypeName(); @@ -89,7 +80,8 @@ public async Task SendAsync(RoutedMessage message, Cancellat props.Headers[Constants.MessageHeaders.MessageType] = typeName; props.Type = typeName; - await Policy.Handle() + await Policy.Handle() + .Or() .WaitAndRetryAsync(_options.MaxFailureRetries, _ => TimeSpan.FromSeconds(3), (exception, _, retryCount, _) => { _logger.LogError(exception, "Retry:{RetryCount}, {Message}", retryCount, exception.Message); @@ -98,7 +90,7 @@ await Policy.Handle() { var messageBody = await SerializeAsync(message, cancellationToken); - channel.BasicPublish("", $"{_options.QueueName}${message.Channel}$", props, messageBody); + channel.BasicPublish("", requestQueueName, props, messageBody); Delivered?.Invoke(this, new MessageDispatchedEventArgs(message.Data, null)); }); @@ -113,21 +105,7 @@ public async Task SendAsync(RoutedMessage SendAsync(RoutedMessage() + await Policy.Handle() + .Or() .WaitAndRetryAsync(_options.MaxFailureRetries, _ => TimeSpan.FromSeconds(1), (exception, _, retryCount, _) => { _logger.LogError(exception, "Retry:{RetryCount}, {Message}", retryCount, exception.Message); @@ -175,6 +154,21 @@ void OnReceived(object sender, BasicDeliverEventArgs args) } } + private static void CheckQueue(IModel channel, string requestQueueName) + { + var queueDeclare = channel.DeclareQueuePassively(requestQueueName); + + if (queueDeclare == null) + { + throw new MessageDeliverException("Channel not found in vhost '/'."); + } + + if (queueDeclare.ConsumerCount < 1) + { + throw new MessageDeliverException("No consumer found for the channel."); + } + } + /// /// Serializes the message to bytes. /// diff --git a/Source/Euonia.Bus.RabbitMq/RabbitMqRecipientRegistrar.cs b/Source/Euonia.Bus.RabbitMq/RabbitMqRecipientRegistrar.cs index 137fdbd..695f5b5 100644 --- a/Source/Euonia.Bus.RabbitMq/RabbitMqRecipientRegistrar.cs +++ b/Source/Euonia.Bus.RabbitMq/RabbitMqRecipientRegistrar.cs @@ -37,7 +37,7 @@ public async Task RegisterAsync(IEnumerable registrations, } else { - throw new InvalidOperationException(); + throw new InvalidOperationException("The message type is neither a queue nor a topic."); } recipient.Start(registration.Channel); diff --git a/Source/Euonia.Bus/BusConfigurator.cs b/Source/Euonia.Bus/BusConfigurator.cs index 38cf9d4..b89c76f 100644 --- a/Source/Euonia.Bus/BusConfigurator.cs +++ b/Source/Euonia.Bus/BusConfigurator.cs @@ -13,7 +13,7 @@ public class BusConfigurator : IBusConfigurator { private readonly List _registrations = new(); - private MessageConventionBuilder ConventionBuilder { get; } = new(); + internal MessageConventionBuilder ConventionBuilder { get; } = new(); /// /// Gets the message handle registrations. @@ -166,7 +166,6 @@ public BusConfigurator RegisterHandlers(IEnumerable types) public BusConfigurator SetConventions(Action configure) { configure?.Invoke(ConventionBuilder); - Service.TryAddSingleton(ConventionBuilder.Convention); return this; } } \ No newline at end of file diff --git a/Source/Euonia.Bus/Core/ServiceBus.cs b/Source/Euonia.Bus/Core/ServiceBus.cs index 8d21501..b87ded0 100644 --- a/Source/Euonia.Bus/Core/ServiceBus.cs +++ b/Source/Euonia.Bus/Core/ServiceBus.cs @@ -43,7 +43,7 @@ public Task PublishAsync(TMessage message, PublishOptions options, Act if (!_convention.IsTopicType(message.GetType())) { - throw new InvalidOperationException("The message type is not an event type."); + throw new MessageTypeException("The message type is not an event type."); } var context = GetRequestContext(); @@ -66,7 +66,7 @@ public Task SendAsync(TMessage message, SendOptions options, Action SendAsync(TMessage message, SendOptions if (!_convention.IsQueueType(message.GetType())) { - throw new InvalidOperationException("The message type is not a queue type."); + throw new MessageTypeException("The message type is not a queue type."); } var context = GetRequestContext(); diff --git a/Source/Euonia.Bus/ServiceCollectionExtensions.cs b/Source/Euonia.Bus/ServiceCollectionExtensions.cs index 0fa56fe..5030abd 100644 --- a/Source/Euonia.Bus/ServiceCollectionExtensions.cs +++ b/Source/Euonia.Bus/ServiceCollectionExtensions.cs @@ -29,7 +29,7 @@ public static void AddServiceBus(this IServiceCollection services, Action(); + services.TryAddSingleton(configurator.ConventionBuilder.Convention); services.AddSingleton(); services.AddHostedService(); } diff --git a/Source/Euonia.Business/Rules/DataAnnotationRule.cs b/Source/Euonia.Business/Rules/DataAnnotationRule.cs index bda22ae..82c5d05 100644 --- a/Source/Euonia.Business/Rules/DataAnnotationRule.cs +++ b/Source/Euonia.Business/Rules/DataAnnotationRule.cs @@ -1,5 +1,7 @@ using System.ComponentModel.DataAnnotations; +using ValidationResult = System.ComponentModel.DataAnnotations.ValidationResult; + namespace Nerosoft.Euonia.Business; /// diff --git a/Source/Euonia.Core/Exceptions/BadGatewayException.cs b/Source/Euonia.Core/Exceptions/BadGatewayException.cs index 609ea1e..2548c03 100644 --- a/Source/Euonia.Core/Exceptions/BadGatewayException.cs +++ b/Source/Euonia.Core/Exceptions/BadGatewayException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur when a server acting as a gateway or proxy received an invalid response diff --git a/Source/Euonia.Core/Exceptions/BadRequestException.cs b/Source/Euonia.Core/Exceptions/BadRequestException.cs index d809cb5..8f3f991 100644 --- a/Source/Euonia.Core/Exceptions/BadRequestException.cs +++ b/Source/Euonia.Core/Exceptions/BadRequestException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if request is bad. diff --git a/Source/Euonia.Core/Exceptions/BusinessException.cs b/Source/Euonia.Core/Exceptions/BusinessException.cs index 3bd7b0e..450f3d5 100644 --- a/Source/Euonia.Core/Exceptions/BusinessException.cs +++ b/Source/Euonia.Core/Exceptions/BusinessException.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur during business logic execution. diff --git a/Source/Euonia.Core/Exceptions/ConfigurationException.cs b/Source/Euonia.Core/Exceptions/ConfigurationException.cs index 8e0fa4c..9454633 100644 --- a/Source/Euonia.Core/Exceptions/ConfigurationException.cs +++ b/Source/Euonia.Core/Exceptions/ConfigurationException.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur during application configuration. diff --git a/Source/Euonia.Core/Exceptions/ConflictException.cs b/Source/Euonia.Core/Exceptions/ConflictException.cs index b688818..0407ca8 100644 --- a/Source/Euonia.Core/Exceptions/ConflictException.cs +++ b/Source/Euonia.Core/Exceptions/ConflictException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if conflict. diff --git a/Source/Euonia.Core/Exceptions/ExceptionPrompt.cs b/Source/Euonia.Core/Exceptions/ExceptionPrompt.cs index f7e99fc..f8e4f5c 100644 --- a/Source/Euonia.Core/Exceptions/ExceptionPrompt.cs +++ b/Source/Euonia.Core/Exceptions/ExceptionPrompt.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Responsible for storing and returning exception prompts. diff --git a/Source/Euonia.Core/Exceptions/ForbiddenException.cs b/Source/Euonia.Core/Exceptions/ForbiddenException.cs index 88f9fd2..bfa814e 100644 --- a/Source/Euonia.Core/Exceptions/ForbiddenException.cs +++ b/Source/Euonia.Core/Exceptions/ForbiddenException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if request is denied. diff --git a/Source/Euonia.Core/Exceptions/GatewayTimeoutException.cs b/Source/Euonia.Core/Exceptions/GatewayTimeoutException.cs index 83ccc2b..3997440 100644 --- a/Source/Euonia.Core/Exceptions/GatewayTimeoutException.cs +++ b/Source/Euonia.Core/Exceptions/GatewayTimeoutException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if gateway timeout. diff --git a/Source/Euonia.Core/Exceptions/HttpStatusCodeAttribute.cs b/Source/Euonia.Core/Exceptions/HttpStatusCodeAttribute.cs index 3400d05..6e0367f 100644 --- a/Source/Euonia.Core/Exceptions/HttpStatusCodeAttribute.cs +++ b/Source/Euonia.Core/Exceptions/HttpStatusCodeAttribute.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] diff --git a/Source/Euonia.Core/Exceptions/HttpStatusException.cs b/Source/Euonia.Core/Exceptions/HttpStatusException.cs index 8206dc3..cc2ba9a 100644 --- a/Source/Euonia.Core/Exceptions/HttpStatusException.cs +++ b/Source/Euonia.Core/Exceptions/HttpStatusException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur when HTTP status code is not 200. diff --git a/Source/Euonia.Core/Exceptions/IExceptionPrompt.cs b/Source/Euonia.Core/Exceptions/IExceptionPrompt.cs index 3274be1..05169b2 100644 --- a/Source/Euonia.Core/Exceptions/IExceptionPrompt.cs +++ b/Source/Euonia.Core/Exceptions/IExceptionPrompt.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// To be added. diff --git a/Source/Euonia.Core/Exceptions/InternalServerErrorException.cs b/Source/Euonia.Core/Exceptions/InternalServerErrorException.cs index 60488c4..958b579 100644 --- a/Source/Euonia.Core/Exceptions/InternalServerErrorException.cs +++ b/Source/Euonia.Core/Exceptions/InternalServerErrorException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents the exception that is thrown when an internal server error occurs. diff --git a/Source/Euonia.Core/Exceptions/InvalidValueException.cs b/Source/Euonia.Core/Exceptions/InvalidValueException.cs index fe260ae..373ebaa 100644 --- a/Source/Euonia.Core/Exceptions/InvalidValueException.cs +++ b/Source/Euonia.Core/Exceptions/InvalidValueException.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents the errors occurring when a value is invalid. diff --git a/Source/Euonia.Core/Exceptions/MethodNotAllowedException.cs b/Source/Euonia.Core/Exceptions/MethodNotAllowedException.cs index 56f1fc7..acb887d 100644 --- a/Source/Euonia.Core/Exceptions/MethodNotAllowedException.cs +++ b/Source/Euonia.Core/Exceptions/MethodNotAllowedException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if request method is not allowed. diff --git a/Source/Euonia.Core/Exceptions/NotFoundException.cs b/Source/Euonia.Core/Exceptions/NotFoundException.cs index b89ba02..c5370f7 100644 --- a/Source/Euonia.Core/Exceptions/NotFoundException.cs +++ b/Source/Euonia.Core/Exceptions/NotFoundException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if data is not found. diff --git a/Source/Euonia.Core/Exceptions/RequestTimeoutException.cs b/Source/Euonia.Core/Exceptions/RequestTimeoutException.cs index 7b529eb..a7450b6 100644 --- a/Source/Euonia.Core/Exceptions/RequestTimeoutException.cs +++ b/Source/Euonia.Core/Exceptions/RequestTimeoutException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if request timeout. diff --git a/Source/Euonia.Core/Exceptions/ServiceUnavailableException.cs b/Source/Euonia.Core/Exceptions/ServiceUnavailableException.cs index 26d77dd..0a93e36 100644 --- a/Source/Euonia.Core/Exceptions/ServiceUnavailableException.cs +++ b/Source/Euonia.Core/Exceptions/ServiceUnavailableException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if service is unavailable. diff --git a/Source/Euonia.Core/Exceptions/TooManyRequestsException.cs b/Source/Euonia.Core/Exceptions/TooManyRequestsException.cs index 6e1ef61..f95e86f 100644 --- a/Source/Euonia.Core/Exceptions/TooManyRequestsException.cs +++ b/Source/Euonia.Core/Exceptions/TooManyRequestsException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if too many requests. diff --git a/Source/Euonia.Core/Exceptions/UpgradeRequiredException.cs b/Source/Euonia.Core/Exceptions/UpgradeRequiredException.cs index fda8a82..60ad60a 100644 --- a/Source/Euonia.Core/Exceptions/UpgradeRequiredException.cs +++ b/Source/Euonia.Core/Exceptions/UpgradeRequiredException.cs @@ -1,6 +1,6 @@ using System.Net; -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents errors that occur if upgrade required. diff --git a/Source/Euonia.Core/Exceptions/ValidationException.cs b/Source/Euonia.Core/Exceptions/ValidationException.cs index b92eee6..c49b203 100644 --- a/Source/Euonia.Core/Exceptions/ValidationException.cs +++ b/Source/Euonia.Core/Exceptions/ValidationException.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// Represents the exception that is thrown when given data is not valid. diff --git a/Source/Euonia.Core/Exceptions/ValidationResult.cs b/Source/Euonia.Core/Exceptions/ValidationResult.cs index 0ca8d6a..b27af99 100644 --- a/Source/Euonia.Core/Exceptions/ValidationResult.cs +++ b/Source/Euonia.Core/Exceptions/ValidationResult.cs @@ -1,4 +1,4 @@ -namespace Nerosoft.Euonia.Core; +namespace System; /// /// The validation result. diff --git a/Source/Euonia.Modularity/Extensions/ServiceProviderExtensions.cs b/Source/Euonia.Modularity/Extensions/ServiceProviderExtensions.cs index 134b3a6..0533c88 100644 --- a/Source/Euonia.Modularity/Extensions/ServiceProviderExtensions.cs +++ b/Source/Euonia.Modularity/Extensions/ServiceProviderExtensions.cs @@ -12,7 +12,7 @@ public static class ServiceProviderExtensions /// The registered service name. /// The service type. /// - public static TService GetService(this IServiceProvider provider, string name) + public static TService GetNamedService(this IServiceProvider provider, string name) where TService : class { var @delegate = (NamedService)provider.GetService(typeof(NamedService)); @@ -27,7 +27,7 @@ public static TService GetService(this IServiceProvider provider, stri /// The service type. /// /// Throws if service with specified name was not resolved. - public static TService GetRequiredService(this IServiceProvider provider, string name) + public static TService GetNamedRequiredService(this IServiceProvider provider, string name) where TService : class { var @delegate = (NamedService)provider.GetService(typeof(NamedService)); diff --git a/Source/Euonia.Repository.EfCore/DataContextBase.cs b/Source/Euonia.Repository.EfCore/DataContextBase.cs index d70a0cf..058dbfd 100644 --- a/Source/Euonia.Repository.EfCore/DataContextBase.cs +++ b/Source/Euonia.Repository.EfCore/DataContextBase.cs @@ -10,261 +10,182 @@ namespace Nerosoft.Euonia.Repository.EfCore; /// public abstract class DataContextBase : DbContext, IRepositoryContext - where TContext : DbContext, IRepositoryContext + where TContext : DbContext, IRepositoryContext { - private readonly ILogger _logger; + /// + protected DataContextBase(DbContextOptions options) + : base(options) + { + Id = Guid.NewGuid(); + } - /// - protected DataContextBase(DbContextOptions options, ILoggerFactory factory) - : base(options) - { - _logger = factory.CreateLogger(); - } - - /// - /// Gets a value indicate whether the entry values are automatically set or not. - /// - protected abstract bool AutoSetEntryValues { get; } - - /// - /// Gets a value indicate whether the domain events publishing are enabled or not. - /// - protected abstract bool EnabledPublishEvents { get; } + /// + /// Gets a value indicate whether the entry values are automatically set or not. + /// + protected abstract bool AutoSetEntryValues { get; } /// /// Gets the kind of the date time. /// protected virtual DateTimeKind DateTimeKind { get; } = DateTimeKind.Unspecified; - /// - public override int SaveChanges() - { - return SaveChanges(true); - } - - /// - public override int SaveChanges(bool acceptAllChangesOnSuccess) - { - var entries = ChangeTracker.Entries(); - var events = GetTrackedEvents(entries); - SetEntryValues(entries); - var result = base.SaveChanges(acceptAllChangesOnSuccess); - if (result > 0 && events.Any()) - { - PublishEvents(events); - } - - return result; - } - - #region Implementation of IRepositoryContext - - /// - /// - /// - public Guid Id => Guid.NewGuid(); - - /// - /// - /// - public virtual string Provider => Database.ProviderName; + /// + public override int SaveChanges() + { + return SaveChanges(true); + } - /// - public IQueryable SetOf() - where TEntity : class - { - return Set(); - } + /// + public override int SaveChanges(bool acceptAllChangesOnSuccess) + { + var entries = ChangeTracker.Entries(); + SetEntryValues(entries); + var result = base.SaveChanges(acceptAllChangesOnSuccess); + return result; + } - /// - public IDbConnection GetConnection() - { - return Database.GetDbConnection(); - } + #region Implementation of IRepositoryContext - /// - public IDbTransaction GetTransaction() - { - return Database.CurrentTransaction?.GetDbTransaction(); - } + /// + /// + /// + public Guid Id { get; } - /// - public async Task RollbackAsync(CancellationToken cancellationToken = default) - { - await Database.RollbackTransactionAsync(cancellationToken); - } + /// + /// + /// + public virtual string Provider => Database.ProviderName; + + /// + public IQueryable SetOf() + where TEntity : class + { + return Set(); + } + + /// + public IDbConnection GetConnection() + { + return Database.GetDbConnection(); + } + + /// + public IDbTransaction GetTransaction() + { + return Database.CurrentTransaction?.GetDbTransaction(); + } + + /// + public async Task RollbackAsync(CancellationToken cancellationToken = default) + { + await Database.RollbackTransactionAsync(cancellationToken); + } #endregion /// public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) - { - return await SaveChangesAsync(true, cancellationToken); - } - - /// - public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) - { - var entries = ChangeTracker.Entries(); - var events = GetTrackedEvents(entries); - SetEntryValues(entries); - var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); - if (result > 0 && events.Any()) - { - PublishEvents(events); - } - - return result; - } - - /// - /// - /// - /// - /// - protected virtual IEnumerable GetTrackedEvents(IEnumerable entries) - { - var events = new List(); - - foreach (var entry in entries) - { - if (entry.Entity is not IHasDomainEvents aggregate) - { - continue; - } - - aggregate.AttachToEvents(); - events.AddRange(aggregate.GetEvents()); - aggregate.ClearEvents(); - } + { + return await SaveChangesAsync(true, cancellationToken); + } + + /// + public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) + { + var entries = ChangeTracker.Entries(); + SetEntryValues(entries); + var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); + return result; + } - return events; - } - - /// - /// Sets the entry values. - /// - /// - protected virtual void SetEntryValues(IEnumerable entries) - { - if (!AutoSetEntryValues) - { - return; - } - - foreach (var entry in entries) - { + /// + /// Sets the entry values. + /// + /// + protected virtual void SetEntryValues(IEnumerable entries) + { + if (!AutoSetEntryValues) + { + return; + } + + foreach (var entry in entries) + { var time = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Unspecified); switch (entry.State) - { - case EntityState.Added: - if (entry.Entity is IHasCreateTime) - { - entry.CurrentValues[nameof(IHasCreateTime.CreateTime)] = time; - } - - if (entry.Entity is IHasUpdateTime) - { - entry.CurrentValues[nameof(IHasUpdateTime.UpdateTime)] = time; - } - - if (entry.Entity is ITombstone) - { - entry.CurrentValues[nameof(ITombstone.IsDeleted)] = false; - } - - break; - case EntityState.Deleted: - if (entry.Entity is ITombstone) - { - entry.State = EntityState.Modified; - entry.CurrentValues[nameof(ITombstone.IsDeleted)] = true; - entry.CurrentValues[nameof(ITombstone.DeleteTime)] = time; - } - - break; - case EntityState.Detached: - break; - case EntityState.Unchanged: - break; - case EntityState.Modified: - SetModifiedEntry(entry, time); - - break; - default: - continue; - } - } - } - - private static void SetModifiedEntry(EntityEntry entry, DateTime time) - { - if (entry.State != EntityState.Modified) - { - return; - } - - // ReSharper disable once ConvertIfStatementToSwitchStatement - if (entry.Entity is ITombstone { IsDeleted: true }) - { - entry.CurrentValues[nameof(ITombstone.IsDeleted)] = true; - entry.CurrentValues[nameof(ITombstone.DeleteTime)] = time; - return; - } - - if (entry.Entity is IHasUpdateTime) - { - entry.CurrentValues[nameof(IHasUpdateTime.UpdateTime)] = time; - } - } - - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - foreach (var entityType in modelBuilder.Model.GetEntityTypes()) - { - //other automated configurations left out - if (typeof(ITombstone).IsAssignableFrom(entityType.ClrType)) - { - entityType.SetTombstoneQueryFilter(); - } - } - } - - /// - /// Publishes the domain events. - /// - /// - protected virtual void PublishEvents(IEnumerable events) - { - if (!EnabledPublishEvents) - { - return; - } - - try - { - async void Action(DomainEvent @event) - { - await PublishEventAsync(@event); - } - - Parallel.ForEach(events, Action); - } - catch (Exception exception) - { - _logger.LogError(exception, "PublishEvents Error"); - Debug.WriteLine(exception); - } - } - - /// - /// Publishes the domain event asynchronously. - /// - /// - /// - /// - protected abstract Task PublishEventAsync(TEvent @event) - where TEvent : DomainEvent; + { + case EntityState.Added: + if (entry.Entity is IHasCreateTime) + { + entry.CurrentValues[nameof(IHasCreateTime.CreateTime)] = time; + } + + if (entry.Entity is IHasUpdateTime) + { + entry.CurrentValues[nameof(IHasUpdateTime.UpdateTime)] = time; + } + + if (entry.Entity is ITombstone) + { + entry.CurrentValues[nameof(ITombstone.IsDeleted)] = false; + } + + break; + case EntityState.Deleted: + if (entry.Entity is ITombstone) + { + entry.State = EntityState.Modified; + entry.CurrentValues[nameof(ITombstone.IsDeleted)] = true; + entry.CurrentValues[nameof(ITombstone.DeleteTime)] = time; + } + + break; + case EntityState.Detached: + break; + case EntityState.Unchanged: + break; + case EntityState.Modified: + SetModifiedEntry(entry, time); + + break; + default: + continue; + } + } + } + + private static void SetModifiedEntry(EntityEntry entry, DateTime time) + { + if (entry.State != EntityState.Modified) + { + return; + } + + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (entry.Entity is ITombstone { IsDeleted: true }) + { + entry.CurrentValues[nameof(ITombstone.IsDeleted)] = true; + entry.CurrentValues[nameof(ITombstone.DeleteTime)] = time; + return; + } + + if (entry.Entity is IHasUpdateTime) + { + entry.CurrentValues[nameof(IHasUpdateTime.UpdateTime)] = time; + } + } + + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + //other automated configurations left out + if (typeof(ITombstone).IsAssignableFrom(entityType.ClrType)) + { + entityType.SetTombstoneQueryFilter(); + } + } + } } \ No newline at end of file diff --git a/Source/Euonia.Validation/DefaultValidator.cs b/Source/Euonia.Validation/DefaultValidator.cs index 1a6343a..25cbe5b 100644 --- a/Source/Euonia.Validation/DefaultValidator.cs +++ b/Source/Euonia.Validation/DefaultValidator.cs @@ -1,7 +1,6 @@ using FluentValidation; -using Nerosoft.Euonia.Core; -using ValidationException = Nerosoft.Euonia.Core.ValidationException; +using ValidationException = System.ValidationException; namespace Nerosoft.Euonia.Validation; diff --git a/project.props b/project.props index 5e21625..6ae9c62 100644 --- a/project.props +++ b/project.props @@ -1,6 +1,6 @@ - 8.1.0 + 8.1.1 damon Nerosoft Ltd. Euonia @@ -8,6 +8,6 @@ enable disable latest - CS2002;IDE0290;IDE0300;IDE0301;IDE0028 + CS2002;IDE0290;IDE0300;IDE0301;IDE0305;IDE0028 \ No newline at end of file