mirror of https://github.com/abpframework/abp.git
2 changed files with 356 additions and 0 deletions
@ -0,0 +1,284 @@ |
|||
using System.Collections.ObjectModel; |
|||
using System.Linq.Expressions; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace System.Linq |
|||
{ |
|||
// Codes below are taken from https://github.com/scottksmith95/LINQKit project.
|
|||
|
|||
/// <summary> The Predicate Operator </summary>
|
|||
public enum PredicateOperator |
|||
{ |
|||
/// <summary> The "Or" </summary>
|
|||
Or, |
|||
|
|||
/// <summary> The "And" </summary>
|
|||
And |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// See http://www.albahari.com/expressions for information and examples.
|
|||
/// </summary>
|
|||
public static class PredicateBuilder |
|||
{ |
|||
private class RebindParameterVisitor : ExpressionVisitor |
|||
{ |
|||
private readonly ParameterExpression _oldParameter; |
|||
private readonly ParameterExpression _newParameter; |
|||
|
|||
public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) |
|||
{ |
|||
_oldParameter = oldParameter; |
|||
_newParameter = newParameter; |
|||
} |
|||
|
|||
protected override Expression VisitParameter(ParameterExpression node) |
|||
{ |
|||
if (node == _oldParameter) |
|||
{ |
|||
return _newParameter; |
|||
} |
|||
|
|||
return base.VisitParameter(node); |
|||
} |
|||
} |
|||
|
|||
/// <summary> Start an expression </summary>
|
|||
public static ExpressionStarter<T> New<T>(Expression<Func<T, bool>> expr = null) |
|||
{ |
|||
return new ExpressionStarter<T>(expr); |
|||
} |
|||
|
|||
/// <summary> Create an expression with a stub expression true or false to use when the expression is not yet started. </summary>
|
|||
public static ExpressionStarter<T> New<T>(bool defaultExpression) |
|||
{ |
|||
return new ExpressionStarter<T>(defaultExpression); |
|||
} |
|||
|
|||
/// <summary> Always true </summary>
|
|||
[Obsolete("Use PredicateBuilder.New() instead.")] |
|||
public static Expression<Func<T, bool>> True<T>() |
|||
{ |
|||
return new ExpressionStarter<T>(true); |
|||
} |
|||
|
|||
/// <summary> Always false </summary>
|
|||
[Obsolete("Use PredicateBuilder.New() instead.")] |
|||
public static Expression<Func<T, bool>> False<T>() |
|||
{ |
|||
return new ExpressionStarter<T>(false); |
|||
} |
|||
|
|||
/// <summary> OR </summary>
|
|||
public static Expression<Func<T, bool>> Or<T>([NotNull] this Expression<Func<T, bool>> expr1, |
|||
[NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); |
|||
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters); |
|||
} |
|||
|
|||
/// <summary> AND </summary>
|
|||
public static Expression<Func<T, bool>> And<T>([NotNull] this Expression<Func<T, bool>> expr1, |
|||
[NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); |
|||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
/// <param name="first">The source Predicate.</param>
|
|||
/// <param name="second">The second Predicate.</param>
|
|||
/// <param name="operator">The Operator (can be "And" or "Or").</param>
|
|||
/// <returns>Expression{Func{T, bool}}</returns>
|
|||
public static Expression<Func<T, bool>> Extend<T>([NotNull] this Expression<Func<T, bool>> first, |
|||
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or) |
|||
{ |
|||
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
/// <param name="first">The source Predicate.</param>
|
|||
/// <param name="second">The second Predicate.</param>
|
|||
/// <param name="operator">The Operator (can be "And" or "Or").</param>
|
|||
/// <returns>Expression{Func{T, bool}}</returns>
|
|||
public static Expression<Func<T, bool>> Extend<T>([NotNull] this ExpressionStarter<T> first, |
|||
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or) |
|||
{ |
|||
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type</typeparam>
|
|||
public class ExpressionStarter<T> |
|||
{ |
|||
public ExpressionStarter() : this(false) |
|||
{ |
|||
} |
|||
|
|||
public ExpressionStarter(bool defaultExpression) |
|||
{ |
|||
if (defaultExpression) |
|||
DefaultExpression = f => true; |
|||
else |
|||
DefaultExpression = f => false; |
|||
} |
|||
|
|||
public ExpressionStarter(Expression<Func<T, bool>> exp) : this(false) |
|||
{ |
|||
_predicate = exp; |
|||
} |
|||
|
|||
/// <summary>The actual Predicate. It can only be set by calling Start.</summary>
|
|||
private Expression<Func<T, bool>> Predicate => |
|||
(IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression; |
|||
|
|||
private Expression<Func<T, bool>> _predicate; |
|||
|
|||
/// <summary>Determines if the predicate is started.</summary>
|
|||
public bool IsStarted => _predicate != null; |
|||
|
|||
/// <summary> A default expression to use only when the expression is null </summary>
|
|||
public bool UseDefaultExpression => DefaultExpression != null; |
|||
|
|||
/// <summary>The default expression</summary>
|
|||
public Expression<Func<T, bool>> DefaultExpression { get; set; } |
|||
|
|||
/// <summary>Set the Expression predicate</summary>
|
|||
/// <param name="exp">The first expression</param>
|
|||
public Expression<Func<T, bool>> Start(Expression<Func<T, bool>> exp) |
|||
{ |
|||
if (IsStarted) |
|||
{ |
|||
throw new Exception("Predicate cannot be started again."); |
|||
} |
|||
|
|||
return _predicate = exp; |
|||
} |
|||
|
|||
/// <summary>Or</summary>
|
|||
public Expression<Func<T, bool>> Or([NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2); |
|||
} |
|||
|
|||
/// <summary>And</summary>
|
|||
public Expression<Func<T, bool>> And([NotNull] Expression<Func<T, bool>> expr2) |
|||
{ |
|||
return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2); |
|||
} |
|||
|
|||
/// <summary> Show predicate string </summary>
|
|||
public override string ToString() |
|||
{ |
|||
return Predicate?.ToString(); |
|||
} |
|||
|
|||
#region Implicit Operators
|
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator Expression<Func<T, bool>>(ExpressionStarter<T> right) |
|||
{ |
|||
return right?.Predicate; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator Func<T, bool>(ExpressionStarter<T> right) |
|||
{ |
|||
return right == null ? null : |
|||
(right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
|
|||
/// </summary>
|
|||
/// <param name="right"></param>
|
|||
public static implicit operator ExpressionStarter<T>(Expression<Func<T, bool>> right) |
|||
{ |
|||
return right == null ? null : new ExpressionStarter<T>(right); |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region Implement Expression<TDelagate> methods and properties
|
|||
|
|||
#if !(NET35)
|
|||
|
|||
/// <summary></summary>
|
|||
public Func<T, bool> Compile() |
|||
{ |
|||
return Predicate.Compile(); |
|||
} |
|||
#endif
|
|||
|
|||
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
|
|||
/// <summary></summary>
|
|||
public Func<T, bool> Compile(DebugInfoGenerator debugInfoGenerator) { return Predicate.Compile(debugInfoGenerator); } |
|||
|
|||
/// <summary></summary>
|
|||
public Expression<Func<T, bool>> Update(Expression body, IEnumerable<ParameterExpression> parameters) { return Predicate.Update(body, parameters); } |
|||
#endif
|
|||
|
|||
#endregion
|
|||
|
|||
#region Implement LamdaExpression methods and properties
|
|||
|
|||
/// <summary></summary>
|
|||
public Expression Body => Predicate.Body; |
|||
|
|||
|
|||
/// <summary></summary>
|
|||
public ExpressionType NodeType => Predicate.NodeType; |
|||
|
|||
/// <summary></summary>
|
|||
public ReadOnlyCollection<ParameterExpression> Parameters => Predicate.Parameters; |
|||
|
|||
/// <summary></summary>
|
|||
public Type Type => Predicate.Type; |
|||
|
|||
#if !(NET35)
|
|||
/// <summary></summary>
|
|||
public string Name => Predicate.Name; |
|||
|
|||
/// <summary></summary>
|
|||
public Type ReturnType => Predicate.ReturnType; |
|||
|
|||
/// <summary></summary>
|
|||
public bool TailCall => Predicate.TailCall; |
|||
#endif
|
|||
|
|||
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
|
|||
/// <summary></summary>
|
|||
public void CompileToMethod(MethodBuilder method) { Predicate.CompileToMethod(method); } |
|||
|
|||
/// <summary></summary>
|
|||
public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) { Predicate.CompileToMethod(method, debugInfoGenerator); } |
|||
|
|||
#endif
|
|||
|
|||
#endregion
|
|||
|
|||
#region Implement Expression methods and properties
|
|||
|
|||
#if !(NET35)
|
|||
/// <summary></summary>
|
|||
public virtual bool CanReduce => Predicate.CanReduce; |
|||
#endif
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace System.Linq |
|||
{ |
|||
public class PredicateBuilder_Tests |
|||
{ |
|||
[Fact] |
|||
public void Test1() |
|||
{ |
|||
var args = new TestArgs(); |
|||
var predicate = PredicateBuilder.New<TestObj>(); |
|||
|
|||
predicate = predicate.And(t => args.Value == t.Value); |
|||
|
|||
var func = predicate.Compile(); |
|||
|
|||
args.Value = true; |
|||
var r2 = func(new TestObj { Value = true }); |
|||
r2.ShouldBeTrue(); |
|||
|
|||
args.Value = false; |
|||
var r1 = func(new TestObj { Value = false }); |
|||
r1.ShouldBeTrue(); |
|||
|
|||
args = new TestArgs {Value = true}; |
|||
var r3 = func(new TestObj { Value = false }); |
|||
r3.ShouldBeFalse(); |
|||
|
|||
args = new TestArgs { Value = false }; |
|||
var r4 = func(new TestObj { Value = false }); |
|||
r4.ShouldBeTrue(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Test2() |
|||
{ |
|||
var args = new TestArgs(); |
|||
var predicate = PredicateBuilder.New<TestObj>(); |
|||
|
|||
predicate = predicate.And(t => !args.Value); |
|||
|
|||
var func = predicate.Compile(); |
|||
|
|||
args.Value = true; |
|||
var r2 = func(new TestObj { Value = true }); |
|||
r2.ShouldBeFalse(); |
|||
|
|||
args.Value = false; |
|||
var r1 = func(new TestObj { Value = false }); |
|||
r1.ShouldBeTrue(); |
|||
|
|||
args = new TestArgs { Value = true }; |
|||
var r3 = func(new TestObj { Value = false }); |
|||
r3.ShouldBeFalse(); |
|||
|
|||
args = new TestArgs { Value = false }; |
|||
var r4 = func(new TestObj { Value = false }); |
|||
r4.ShouldBeTrue(); |
|||
} |
|||
|
|||
public class TestArgs |
|||
{ |
|||
public bool Value { get; set; } |
|||
} |
|||
|
|||
public class TestObj |
|||
{ |
|||
public bool Value { get; set; } |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue