mirror of https://github.com/abpframework/abp.git
committed by
GitHub
20 changed files with 766 additions and 1 deletions
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.Specifications</AssemblyName> |
|||
<PackageId>Volo.Abp.Specifications</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
public class AbpSpecificationsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the combined specification which indicates that the first specification
|
|||
/// can be satisifed by the given object whereas the second one cannot.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public class AndNotSpecification<T> : CompositeSpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Constructs a new instance of <see cref="AndNotSpecification{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="left">The first specification.</param>
|
|||
/// <param name="right">The second specification.</param>
|
|||
public AndNotSpecification(ISpecification<T> left, ISpecification<T> right) : base(left, right) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
var rightExpression = Right.ToExpression(); |
|||
|
|||
var bodyNot = Expression.Not(rightExpression.Body); |
|||
var bodyNotExpression = Expression.Lambda<Func<T, bool>>(bodyNot, rightExpression.Parameters); |
|||
|
|||
return Left.ToExpression().And(bodyNotExpression); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the combined specification which indicates that both of the given
|
|||
/// specifications should be satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public class AndSpecification<T> : CompositeSpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Constructs a new instance of <see cref="AndSpecification{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="left">The first specification.</param>
|
|||
/// <param name="right">The second specification.</param>
|
|||
public AndSpecification(ISpecification<T> left, ISpecification<T> right) : base(left, right) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
return Left.ToExpression().And(Right.ToExpression()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the specification that can be satisfied by the given object
|
|||
/// in any circumstance.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public sealed class AnySpecification<T> : Specification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
return o => true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the base class for composite specifications.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public abstract class CompositeSpecification<T> : Specification<T>, ICompositeSpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Constructs a new instance of <see cref="CompositeSpecification{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="left">The first specification.</param>
|
|||
/// <param name="right">The second specification.</param>
|
|||
protected CompositeSpecification(ISpecification<T> left, ISpecification<T> right) |
|||
{ |
|||
Left = left; |
|||
Right = right; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the first specification.
|
|||
/// </summary>
|
|||
public ISpecification<T> Left { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the second specification.
|
|||
/// </summary>
|
|||
public ISpecification<T> Right { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the extender for Expression[Func[T, bool]] type.
|
|||
/// This is part of the solution which solves
|
|||
/// the expression parameter problem when going to Entity Framework.
|
|||
/// For more information about this solution please refer to http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx.
|
|||
/// </summary>
|
|||
public static class ExpressionFuncExtender |
|||
{ |
|||
private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, |
|||
Func<Expression, Expression, Expression> merge) |
|||
{ |
|||
// build parameter map (from parameters of second to parameters of first)
|
|||
var map = first.Parameters.Select((f, i) => new {f, s = second.Parameters[i]}) |
|||
.ToDictionary(p => p.s, p => p.f); |
|||
|
|||
// replace parameters in the second lambda expression with parameters from the first
|
|||
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); |
|||
|
|||
// apply composition of lambda expression bodies to parameters from the first expression
|
|||
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Combines two given expressions by using the AND semantics.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object.</typeparam>
|
|||
/// <param name="first">The first part of the expression.</param>
|
|||
/// <param name="second">The second part of the expression.</param>
|
|||
/// <returns>The combined expression.</returns>
|
|||
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, |
|||
Expression<Func<T, bool>> second) |
|||
{ |
|||
return first.Compose(second, Expression.AndAlso); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Combines two given expressions by using the OR semantics.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object.</typeparam>
|
|||
/// <param name="first">The first part of the expression.</param>
|
|||
/// <param name="second">The second part of the expression.</param>
|
|||
/// <returns>The combined expression.</returns>
|
|||
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, |
|||
Expression<Func<T, bool>> second) |
|||
{ |
|||
return first.Compose(second, Expression.OrElse); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the specification which is represented by the corresponding
|
|||
/// LINQ expression.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public class ExpressionSpecification<T> : Specification<T> |
|||
{ |
|||
private readonly Expression<Func<T, bool>> _expression; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of <c>ExpressionSpecification<T></c> class.
|
|||
/// </summary>
|
|||
/// <param name="expression">The LINQ expression which represents the current
|
|||
/// specification.</param>
|
|||
public ExpressionSpecification(Expression<Func<T, bool>> expression) |
|||
{ |
|||
_expression = expression; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
return _expression; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents that the implemented classes are composite specifications.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public interface ICompositeSpecification<T> : ISpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the left side of the specification.
|
|||
/// </summary>
|
|||
ISpecification<T> Left { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the right side of the specification.
|
|||
/// </summary>
|
|||
ISpecification<T> Right { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents that the implemented classes are specifications. For more
|
|||
/// information about the specification pattern, please refer to
|
|||
/// http://martinfowler.com/apsupp/spec.pdf.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public interface ISpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Returns a <see cref="bool"/> value which indicates whether the specification
|
|||
/// is satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to which the specification is applied.</param>
|
|||
/// <returns>True if the specification is satisfied, otherwise false.</returns>
|
|||
bool IsSatisfiedBy(T obj); |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
Expression<Func<T, bool>> ToExpression(); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents that the implemented classes are specification parsers that
|
|||
/// parses the given specification to a domain specific criteria object, such
|
|||
/// as the <c>ICriteria</c> instance in NHibernate.
|
|||
/// </summary>
|
|||
/// <typeparam name="TCriteria">The type of the domain specific criteria.</typeparam>
|
|||
public interface ISpecificationParser<out TCriteria> |
|||
{ |
|||
/// <summary>
|
|||
/// Parses the given specification to a domain specific criteria object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
/// <param name="specification">The specified specification instance.</param>
|
|||
/// <returns>The instance of the domain specific criteria.</returns>
|
|||
TCriteria Parse<T>(ISpecification<T> specification); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the specification that can be satisfied by the given object
|
|||
/// in no circumstance.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public sealed class NoneSpecification<T> : Specification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
return o => false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the specification which indicates the semantics opposite to the given specification.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public class NotSpecification<T> : Specification<T> |
|||
{ |
|||
private readonly ISpecification<T> _specification; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of <see cref="NotSpecification{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="specification">The specification to be reversed.</param>
|
|||
public NotSpecification(ISpecification<T> specification) |
|||
{ |
|||
_specification = specification; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
var expression = _specification.ToExpression(); |
|||
return Expression.Lambda<Func<T, bool>>( |
|||
Expression.Not(expression.Body), |
|||
expression.Parameters |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the combined specification which indicates that either of the given
|
|||
/// specification should be satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public class OrSpecification<T> : CompositeSpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of <see cref="OrSpecification{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="left">The first specification.</param>
|
|||
/// <param name="right">The second specification.</param>
|
|||
public OrSpecification(ISpecification<T> left, ISpecification<T> right) : base(left, right) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public override Expression<Func<T, bool>> ToExpression() |
|||
{ |
|||
return Left.ToExpression().Or(Right.ToExpression()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the parameter rebinder used for rebinding the parameters
|
|||
/// for the given expressions. This is part of the solution which solves
|
|||
/// the expression parameter problem when going to Entity Framework.
|
|||
/// For more information about this solution please refer to http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx.
|
|||
/// </summary>
|
|||
internal class ParameterRebinder : ExpressionVisitor |
|||
{ |
|||
private readonly Dictionary<ParameterExpression, ParameterExpression> _map; |
|||
|
|||
internal ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) |
|||
{ |
|||
_map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); |
|||
} |
|||
|
|||
internal static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, |
|||
Expression exp) |
|||
{ |
|||
return new ParameterRebinder(map).Visit(exp); |
|||
} |
|||
|
|||
protected override Expression VisitParameter(ParameterExpression p) |
|||
{ |
|||
if (_map.TryGetValue(p, out var replacement)) |
|||
{ |
|||
p = replacement; |
|||
} |
|||
|
|||
return base.VisitParameter(p); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the base class for specifications.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the object to which the specification is applied.</typeparam>
|
|||
public abstract class Specification<T> : ISpecification<T> |
|||
{ |
|||
/// <summary>
|
|||
/// Returns a <see cref="bool"/> value which indicates whether the specification
|
|||
/// is satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <param name="obj">The object to which the specification is applied.</param>
|
|||
/// <returns>True if the specification is satisfied, otherwise false.</returns>
|
|||
public virtual bool IsSatisfiedBy(T obj) |
|||
{ |
|||
return ToExpression().Compile()(obj); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LINQ expression which represents the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The LINQ expression.</returns>
|
|||
public abstract Expression<Func<T, bool>> ToExpression(); |
|||
|
|||
/// <summary>
|
|||
/// Implicitly converts a specification to expression.
|
|||
/// </summary>
|
|||
/// <param name="specification"></param>
|
|||
public static implicit operator Expression<Func<T, bool>>(Specification<T> specification) |
|||
{ |
|||
return specification.ToExpression(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
public static class SpecificationExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Combines the current specification instance with another specification instance
|
|||
/// and returns the combined specification which represents that both the current and
|
|||
/// the given specification must be satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <param name="specification">The specification</param>
|
|||
/// <param name="other">The specification instance with which the current specification is combined.</param>
|
|||
/// <returns>The combined specification instance.</returns>
|
|||
public static ISpecification<T> And<T>([NotNull] this ISpecification<T> specification, |
|||
[NotNull] ISpecification<T> other) |
|||
{ |
|||
Check.NotNull(specification, nameof(specification)); |
|||
Check.NotNull(other, nameof(other)); |
|||
|
|||
return new AndSpecification<T>(specification, other); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Combines the current specification instance with another specification instance
|
|||
/// and returns the combined specification which represents that either the current or
|
|||
/// the given specification should be satisfied by the given object.
|
|||
/// </summary>
|
|||
/// <param name="specification">The specification</param>
|
|||
/// <param name="other">The specification instance with which the current specification
|
|||
/// is combined.</param>
|
|||
/// <returns>The combined specification instance.</returns>
|
|||
public static ISpecification<T> Or<T>([NotNull] this ISpecification<T> specification, |
|||
[NotNull] ISpecification<T> other) |
|||
{ |
|||
Check.NotNull(specification, nameof(specification)); |
|||
Check.NotNull(other, nameof(other)); |
|||
|
|||
return new OrSpecification<T>(specification, other); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Combines the current specification instance with another specification instance
|
|||
/// and returns the combined specification which represents that the current specification
|
|||
/// should be satisfied by the given object but the specified specification should not.
|
|||
/// </summary>
|
|||
/// <param name="specification">The specification</param>
|
|||
/// <param name="other">The specification instance with which the current specification
|
|||
/// is combined.</param>
|
|||
/// <returns>The combined specification instance.</returns>
|
|||
public static ISpecification<T> AndNot<T>([NotNull] this ISpecification<T> specification, |
|||
[NotNull] ISpecification<T> other) |
|||
{ |
|||
Check.NotNull(specification, nameof(specification)); |
|||
Check.NotNull(other, nameof(other)); |
|||
|
|||
return new AndNotSpecification<T>(specification, other); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reverses the current specification instance and returns a specification which represents
|
|||
/// the semantics opposite to the current specification.
|
|||
/// </summary>
|
|||
/// <returns>The reversed specification instance.</returns>
|
|||
public static ISpecification<T> Not<T>([NotNull] this ISpecification<T> specification) |
|||
{ |
|||
Check.NotNull(specification, nameof(specification)); |
|||
|
|||
return new NotSpecification<T>(specification); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp2.2</TargetFramework> |
|||
<LangVersion>latest</LangVersion> |
|||
<AssemblyName>Volo.Abp.Specifications.Tests</AssemblyName> |
|||
<PackageId>Volo.Abp.Specifications.Tests</PackageId> |
|||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Specifications\Volo.Abp.Specifications.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,191 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Expressions; |
|||
using System.Text; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Specifications |
|||
{ |
|||
public class Specification_Tests |
|||
{ |
|||
private readonly IQueryable<Customer> _customers; |
|||
|
|||
public Specification_Tests() |
|||
{ |
|||
_customers = new List<Customer> |
|||
{ |
|||
new Customer("John", 17, 47000, "England"), |
|||
new Customer("Tuana", 2, 500, "Turkey"), |
|||
new Customer("Martin", 43, 16000, "USA"), |
|||
new Customer("Lee", 32, 24502, "China"), |
|||
new Customer("Douglas", 42, 42000, "England"), |
|||
new Customer("Abelard", 14, 2332, "German"), |
|||
new Customer("Neo", 16, 120000, "USA"), |
|||
new Customer("Daan", 39, 6000, "Netherlands"), |
|||
new Customer("Alessandro", 22, 8271, "Italy"), |
|||
new Customer("Noah", 33, 82192, "Belgium") |
|||
}.AsQueryable(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Any_Should_Return_All() |
|||
{ |
|||
_customers |
|||
.Where(new AnySpecification<Customer>()) //Implicitly converted to Expression!
|
|||
.Count() |
|||
.ShouldBe(_customers.Count()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void None_Should_Return_Empty() |
|||
{ |
|||
_customers |
|||
.Where(new NoneSpecification<Customer>().ToExpression()) |
|||
.Count() |
|||
.ShouldBe(0); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Not_Should_Return_Reverse_Result() |
|||
{ |
|||
_customers |
|||
.Where(new EuropeanCustomerSpecification().Not().ToExpression()) |
|||
.Count() |
|||
.ShouldBe(3); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Support_Native_Expressions_And_Combinations() |
|||
{ |
|||
_customers |
|||
.Where(new ExpressionSpecification<Customer>(c => c.Age >= 18).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(6); |
|||
|
|||
_customers |
|||
.Where(new EuropeanCustomerSpecification().And(new ExpressionSpecification<Customer>(c => c.Age >= 18)).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(4); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CustomSpecification_Test() |
|||
{ |
|||
_customers |
|||
.Where(new EuropeanCustomerSpecification().ToExpression()) |
|||
.Count() |
|||
.ShouldBe(7); |
|||
|
|||
_customers |
|||
.Where(new Age18PlusCustomerSpecification().ToExpression()) |
|||
.Count() |
|||
.ShouldBe(6); |
|||
|
|||
_customers |
|||
.Where(new BalanceCustomerSpecification(10000, 30000).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(2); |
|||
|
|||
_customers |
|||
.Where(new PremiumCustomerSpecification().ToExpression()) |
|||
.Count() |
|||
.ShouldBe(3); |
|||
} |
|||
|
|||
[Fact] |
|||
public void IsSatisfiedBy_Tests() |
|||
{ |
|||
new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 49, 55000, "USA")).ShouldBeTrue(); |
|||
|
|||
new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 49, 200, "USA")).ShouldBeFalse(); |
|||
new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 12, 55000, "USA")).ShouldBeFalse(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CustomSpecification_Composite_Tests() |
|||
{ |
|||
_customers |
|||
.Where(new EuropeanCustomerSpecification().And(new Age18PlusCustomerSpecification()).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(4); |
|||
|
|||
_customers |
|||
.Where(new EuropeanCustomerSpecification().Not().And(new Age18PlusCustomerSpecification()).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(2); |
|||
|
|||
_customers |
|||
.Where(new Age18PlusCustomerSpecification().AndNot(new EuropeanCustomerSpecification()).ToExpression()) |
|||
.Count() |
|||
.ShouldBe(2); |
|||
} |
|||
|
|||
private class Customer |
|||
{ |
|||
public string Name { get; private set; } |
|||
|
|||
public byte Age { get; private set; } |
|||
|
|||
public long Balance { get; private set; } |
|||
|
|||
public string Location { get; private set; } |
|||
|
|||
public Customer(string name, byte age, long balance, string location) |
|||
{ |
|||
Name = name; |
|||
Age = age; |
|||
Balance = balance; |
|||
Location = location; |
|||
} |
|||
} |
|||
|
|||
private class EuropeanCustomerSpecification : Specification<Customer> |
|||
{ |
|||
public override Expression<Func<Customer, bool>> ToExpression() |
|||
{ |
|||
return c => c.Location == "England" || |
|||
c.Location == "Turkey" || |
|||
c.Location == "German" || |
|||
c.Location == "Netherlands" || |
|||
c.Location == "Italy" || |
|||
c.Location == "Belgium"; |
|||
} |
|||
} |
|||
|
|||
private class Age18PlusCustomerSpecification : Specification<Customer> |
|||
{ |
|||
public override Expression<Func<Customer, bool>> ToExpression() |
|||
{ |
|||
return c => c.Age >= 18; |
|||
} |
|||
} |
|||
|
|||
private class BalanceCustomerSpecification : Specification<Customer> |
|||
{ |
|||
public int MinBalance { get; } |
|||
|
|||
public int MaxBalance { get; } |
|||
|
|||
public BalanceCustomerSpecification(int minBalance, int maxBalance) |
|||
{ |
|||
MinBalance = minBalance; |
|||
MaxBalance = maxBalance; |
|||
} |
|||
|
|||
public override Expression<Func<Customer, bool>> ToExpression() |
|||
{ |
|||
return c => c.Balance >= MinBalance && c.Balance <= MaxBalance; |
|||
} |
|||
} |
|||
|
|||
private class PremiumCustomerSpecification : AndSpecification<Customer> |
|||
{ |
|||
public PremiumCustomerSpecification() |
|||
: base(new Age18PlusCustomerSpecification(), new BalanceCustomerSpecification(20000, int.MaxValue)) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue