diff --git a/src/Fluxera.DomainEvents.Abstractions/Fluxera.DomainEvents.Abstractions.csproj b/src/Fluxera.DomainEvents.Abstractions/Fluxera.DomainEvents.Abstractions.csproj index de045f9..a620359 100644 --- a/src/Fluxera.DomainEvents.Abstractions/Fluxera.DomainEvents.Abstractions.csproj +++ b/src/Fluxera.DomainEvents.Abstractions/Fluxera.DomainEvents.Abstractions.csproj @@ -27,7 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.DomainEvents.Abstractions/Guard.cs b/src/Fluxera.DomainEvents.Abstractions/Guard.cs new file mode 100644 index 0000000..3c1e0f2 --- /dev/null +++ b/src/Fluxera.DomainEvents.Abstractions/Guard.cs @@ -0,0 +1,122 @@ +namespace Fluxera.DomainEvents.Abstractions +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Numerics; + using System.Runtime.CompilerServices; + using JetBrains.Annotations; + + internal static class Guard + { + public static T ThrowIfNull(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + ArgumentNullException.ThrowIfNull(argument, parameterName); + + return argument; + } + + public static string ThrowIfNullOrEmpty(string argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrEmpty(argument)) + { + throw new ArgumentException("Value cannot be empty.", parameterName); + } + + return argument; + } + + public static string ThrowIfNullOrWhiteSpace(string argument, [InvokerParameterName][CallerArgumentExpression("argument")] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrWhiteSpace(argument)) + { + throw new ArgumentException("Value cannot be whitespace-only.", parameterName); + } + + return argument; + } + + public static bool ThrowIfFalse(bool argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null, string message = null) + { + if(!argument) + { + throw new ArgumentException(message ?? "Value cannot be false.", parameterName); + } + + return true; + } + + public static IEnumerable ThrowIfNullOrEmpty(IEnumerable argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + // ReSharper disable PossibleMultipleEnumeration + if(!argument.Any()) + { + throw new ArgumentException("Enumerable cannot be empty.", parameterName); + } + + return argument; + // ReSharper enable PossibleMultipleEnumeration + } + +#if NET7_0_OR_GREATER + public static T ThrowIfNegative(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + where T : INumber + { + if(T.IsNegative(argument)) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + +#if NET6_0 + public static byte ThrowIfNegative(byte argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static short ThrowIfNegative(short argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static int ThrowIfNegative(int argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static long ThrowIfNegative(long argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + } +} diff --git a/src/Fluxera.DomainEvents.MediatR/Fluxera.DomainEvents.MediatR.csproj b/src/Fluxera.DomainEvents.MediatR/Fluxera.DomainEvents.MediatR.csproj index 4c0fec2..df81c7a 100644 --- a/src/Fluxera.DomainEvents.MediatR/Fluxera.DomainEvents.MediatR.csproj +++ b/src/Fluxera.DomainEvents.MediatR/Fluxera.DomainEvents.MediatR.csproj @@ -22,13 +22,12 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.DomainEvents.MediatR/ServiceCollectionExtensions.cs b/src/Fluxera.DomainEvents.MediatR/ServiceCollectionExtensions.cs index 72feafe..ed318fa 100644 --- a/src/Fluxera.DomainEvents.MediatR/ServiceCollectionExtensions.cs +++ b/src/Fluxera.DomainEvents.MediatR/ServiceCollectionExtensions.cs @@ -1,7 +1,6 @@ namespace Fluxera.DomainEvents.MediatR { using Fluxera.DomainEvents.Abstractions; - using Fluxera.Guards; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -19,7 +18,7 @@ public static class ServiceCollectionExtensions /// public static IServiceCollection AddDomainEvents(this IServiceCollection services) { - services = Guard.Against.Null(services); + services = Guard.ThrowIfNull(services); // Register the default domain event dispatcher. services.AddDomainEventDispatcher(); @@ -36,7 +35,7 @@ public static IServiceCollection AddDomainEvents(this IServiceCollection service public static IServiceCollection AddDomainEventDispatcher(this IServiceCollection services) where TDispatcher : MediatrDomainEventDispatcher { - services = Guard.Against.Null(services); + services = Guard.ThrowIfNull(services); services.RemoveAll(); services.AddScoped(); diff --git a/src/Fluxera.DomainEvents/Fluxera.DomainEvents.csproj b/src/Fluxera.DomainEvents/Fluxera.DomainEvents.csproj index 8888897..025e08f 100644 --- a/src/Fluxera.DomainEvents/Fluxera.DomainEvents.csproj +++ b/src/Fluxera.DomainEvents/Fluxera.DomainEvents.csproj @@ -22,13 +22,12 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.DomainEvents/ServiceCollectionExtensions.cs b/src/Fluxera.DomainEvents/ServiceCollectionExtensions.cs index cb7d586..5c0dd6c 100644 --- a/src/Fluxera.DomainEvents/ServiceCollectionExtensions.cs +++ b/src/Fluxera.DomainEvents/ServiceCollectionExtensions.cs @@ -1,7 +1,6 @@ namespace Fluxera.DomainEvents { using Fluxera.DomainEvents.Abstractions; - using Fluxera.Guards; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -23,7 +22,7 @@ public static class ServiceCollectionExtensions /// public static IServiceCollection AddDomainEvents(this IServiceCollection services) { - services = Guard.Against.Null(services); + services = Guard.ThrowIfNull(services); // Register the default domain event dispatcher. services.AddDomainEventDispatcher(); @@ -40,7 +39,7 @@ public static IServiceCollection AddDomainEvents(this IServiceCollection service public static IServiceCollection AddDomainEventDispatcher(this IServiceCollection services) where TDispatcher : DefaultDomainEventDispatcher { - services = Guard.Against.Null(services); + services = Guard.ThrowIfNull(services); services.RemoveAll(); services.AddScoped(); @@ -56,7 +55,7 @@ public static IServiceCollection AddDomainEventDispatcher(this ISer /// public static IServiceCollection AddDomainEventHandler(this IServiceCollection services) { - services = Guard.Against.Null(services); + services = Guard.ThrowIfNull(services); Type type = typeof(TDomainEventHandler); diff --git a/src/Fluxera.Entity/AggregateRoot.cs b/src/Fluxera.Entity/AggregateRoot.cs index 6dfe6fc..31263ab 100644 --- a/src/Fluxera.Entity/AggregateRoot.cs +++ b/src/Fluxera.Entity/AggregateRoot.cs @@ -3,12 +3,8 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; - using Fluxera.ComponentModel.Annotations; using Fluxera.DomainEvents.Abstractions; using JetBrains.Annotations; -#if NET6_0 - using Fluxera.Utilities.Extensions; -#endif /// /// A base class for aggregate roots. @@ -25,7 +21,6 @@ public abstract class AggregateRoot : Entity /// The domain events of this entity. /// - [Ignore] [IgnoreDataMember] public IReadOnlyCollection DomainEvents => this.domainEvents.AsReadOnly(); diff --git a/src/Fluxera.Entity/Entity.cs b/src/Fluxera.Entity/Entity.cs index 6294c89..7db85d3 100644 --- a/src/Fluxera.Entity/Entity.cs +++ b/src/Fluxera.Entity/Entity.cs @@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; - using Fluxera.ComponentModel.Annotations; using JetBrains.Annotations; /// @@ -36,7 +35,6 @@ public abstract class Entity /// /// Gets a flag, if the entity instance is transient (not stored to the storage). /// - [Ignore] [IgnoreDataMember] public virtual bool IsTransient { diff --git a/src/Fluxera.Entity/EnumerableExtensions.cs b/src/Fluxera.Entity/EnumerableExtensions.cs new file mode 100644 index 0000000..5be4995 --- /dev/null +++ b/src/Fluxera.Entity/EnumerableExtensions.cs @@ -0,0 +1,24 @@ +#if NET6_0 +namespace Fluxera.Entity +{ + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + + internal static class EnumerableExtensions + { + /// + /// Converts the enumerable to a read-only collection. + /// + /// The type of the elements. + /// The collection to write-protect. + /// A write-protected collection. + public static IReadOnlyCollection AsReadOnly(this IEnumerable enumerable) + { + enumerable = Guard.ThrowIfNull(enumerable); + + return new ReadOnlyCollection(enumerable.ToList()); + } + } +} +#endif diff --git a/src/Fluxera.Entity/Fluxera.Entity.csproj b/src/Fluxera.Entity/Fluxera.Entity.csproj index 2ac169b..6036518 100644 --- a/src/Fluxera.Entity/Fluxera.Entity.csproj +++ b/src/Fluxera.Entity/Fluxera.Entity.csproj @@ -22,15 +22,12 @@ - - - all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.Entity/Guard.cs b/src/Fluxera.Entity/Guard.cs new file mode 100644 index 0000000..525324d --- /dev/null +++ b/src/Fluxera.Entity/Guard.cs @@ -0,0 +1,122 @@ +namespace Fluxera.Entity +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Numerics; + using System.Runtime.CompilerServices; + using JetBrains.Annotations; + + internal static class Guard + { + public static T ThrowIfNull(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + ArgumentNullException.ThrowIfNull(argument, parameterName); + + return argument; + } + + public static string ThrowIfNullOrEmpty(string argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrEmpty(argument)) + { + throw new ArgumentException("Value cannot be empty.", parameterName); + } + + return argument; + } + + public static string ThrowIfNullOrWhiteSpace(string argument, [InvokerParameterName][CallerArgumentExpression("argument")] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrWhiteSpace(argument)) + { + throw new ArgumentException("Value cannot be whitespace-only.", parameterName); + } + + return argument; + } + + public static bool ThrowIfFalse(bool argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null, string message = null) + { + if(!argument) + { + throw new ArgumentException(message ?? "Value cannot be false.", parameterName); + } + + return true; + } + + public static IEnumerable ThrowIfNullOrEmpty(IEnumerable argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + // ReSharper disable PossibleMultipleEnumeration + if(!argument.Any()) + { + throw new ArgumentException("Enumerable cannot be empty.", parameterName); + } + + return argument; + // ReSharper enable PossibleMultipleEnumeration + } + +#if NET7_0_OR_GREATER + public static T ThrowIfNegative(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + where T : INumber + { + if(T.IsNegative(argument)) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + +#if NET6_0 + public static byte ThrowIfNegative(byte argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static short ThrowIfNegative(short argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static int ThrowIfNegative(int argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static long ThrowIfNegative(long argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + } +} diff --git a/src/Fluxera.Entity/PropertyAccessor.cs b/src/Fluxera.Entity/PropertyAccessor.cs index c2a7b87..6758a0f 100644 --- a/src/Fluxera.Entity/PropertyAccessor.cs +++ b/src/Fluxera.Entity/PropertyAccessor.cs @@ -7,7 +7,6 @@ namespace Fluxera.Entity using System.Collections.Concurrent; using System.Linq; using System.Reflection; - using Fluxera.Guards; using JetBrains.Annotations; [PublicAPI] @@ -19,8 +18,8 @@ internal sealed class PropertyAccessor private PropertyAccessor(string propertyName, Func getterFunc) { - Guard.Against.NullOrWhiteSpace(propertyName, nameof(propertyName)); - Guard.Against.Null(getterFunc, nameof(getterFunc)); + Guard.ThrowIfNullOrWhiteSpace(propertyName); + Guard.ThrowIfNull(getterFunc); this.PropertyName = propertyName; this.GetterFunc = getterFunc; diff --git a/tests/Fluxera.DomainEvents.UnitTests/EmployeeAggregate/EmployeeStringId.cs b/tests/Fluxera.DomainEvents.UnitTests/EmployeeAggregate/EmployeeStringId.cs index 2882fff..065cd86 100644 --- a/tests/Fluxera.DomainEvents.UnitTests/EmployeeAggregate/EmployeeStringId.cs +++ b/tests/Fluxera.DomainEvents.UnitTests/EmployeeAggregate/EmployeeStringId.cs @@ -2,7 +2,6 @@ { using System.Collections.Generic; using Fluxera.Entity; - using Fluxera.Guards; using JetBrains.Annotations; [PublicAPI] @@ -20,8 +19,6 @@ public class EmployeeStringId : AggregateRoot public void GiveRaise(decimal raiseAmount) { - Guard.Against.NegativeOrZero(raiseAmount, nameof(raiseAmount)); - this.Salary += raiseAmount; this.RaiseDomainEvent(new SalaryRaisedEvent(this.Salary)); diff --git a/tests/Fluxera.DomainEvents.UnitTests/Fluxera.DomainEvents.UnitTests.csproj b/tests/Fluxera.DomainEvents.UnitTests/Fluxera.DomainEvents.UnitTests.csproj index bdce092..940cdab 100644 --- a/tests/Fluxera.DomainEvents.UnitTests/Fluxera.DomainEvents.UnitTests.csproj +++ b/tests/Fluxera.DomainEvents.UnitTests/Fluxera.DomainEvents.UnitTests.csproj @@ -6,6 +6,8 @@ + + diff --git a/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/Employee.cs b/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/Employee.cs index 2bac231..4b489ff 100644 --- a/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/Employee.cs +++ b/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/Employee.cs @@ -1,8 +1,8 @@ namespace Fluxera.Entity.UnitTests.EmployeeAggregate { using System.Collections.Generic; - using Fluxera.Guards; using JetBrains.Annotations; + using Fluxera.Guards; [PublicAPI] public class Employee : AggregateRoot diff --git a/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/EmployeeStringId.cs b/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/EmployeeStringId.cs index 0d5578f..8c00ac2 100644 --- a/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/EmployeeStringId.cs +++ b/tests/Fluxera.Entity.UnitTests/EmployeeAggregate/EmployeeStringId.cs @@ -1,8 +1,8 @@ namespace Fluxera.Entity.UnitTests.EmployeeAggregate { using System.Collections.Generic; - using Fluxera.Guards; using JetBrains.Annotations; + using Fluxera.Guards; [PublicAPI] public class EmployeeStringId : AggregateRoot diff --git a/tests/Fluxera.Entity.UnitTests/Fluxera.Entity.UnitTests.csproj b/tests/Fluxera.Entity.UnitTests/Fluxera.Entity.UnitTests.csproj index 203df59..9a648be 100644 --- a/tests/Fluxera.Entity.UnitTests/Fluxera.Entity.UnitTests.csproj +++ b/tests/Fluxera.Entity.UnitTests/Fluxera.Entity.UnitTests.csproj @@ -6,6 +6,8 @@ + +