From 8b2569b2a52175779ccdfd48c171c995a32935c8 Mon Sep 17 00:00:00 2001
From: Wilson <648178158@qq.com>
Date: Mon, 24 Feb 2020 14:11:45 +0800
Subject: [PATCH 1/2] add Predicate
---
 .../Collections/EnumerableExtensionMethod.cs  | 54 ++++++++++++
 src/UnitOfWork/Internals/PredicateConcater.cs | 83 +++++++++++++++++++
 2 files changed, 137 insertions(+)
 create mode 100644 src/UnitOfWork/Collections/EnumerableExtensionMethod.cs
 create mode 100644 src/UnitOfWork/Internals/PredicateConcater.cs
diff --git a/src/UnitOfWork/Collections/EnumerableExtensionMethod.cs b/src/UnitOfWork/Collections/EnumerableExtensionMethod.cs
new file mode 100644
index 0000000..9dfd2c5
--- /dev/null
+++ b/src/UnitOfWork/Collections/EnumerableExtensionMethod.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Arch.EntityFrameworkCore.UnitOfWork.Collections
+{
+    /// 
+    /// Class EnumerableExtensionMethod.
+    /// 
+    public static class EnumerableExtensionMethod
+    {
+        /// 
+        /// 包裹泛型IEnumerable实例, 避免可能的数组实例造成EF动态过滤失败
+        /// 
+        /// 
+        /// The source.
+        /// IEnumerable<T>.
+        public static IEnumerable WrapEnumerable(this IEnumerable source) => new EnumerableWrapper(source);
+
+        /// 
+        /// Class EnumerableWrapper.
+        /// Implements the 
+        /// 
+        /// 
+        /// 
+        private class EnumerableWrapper : IEnumerable
+        {
+            /// 
+            /// The underlying instance
+            /// 
+            private readonly IEnumerable _underlyingInstance;
+
+            /// 
+            /// Initializes a new instance of the  class.
+            /// 
+            /// The underlying instance.
+            public EnumerableWrapper(IEnumerable underlyingInstance) => _underlyingInstance = underlyingInstance;
+
+            /// 
+            /// 返回一个循环访问集合的枚举器。
+            /// 
+            /// 可用于循环访问集合的 。
+            IEnumerator IEnumerable.GetEnumerator() => _underlyingInstance.GetEnumerator();
+
+            /// 
+            /// Gets the enumerator.
+            /// 
+            /// IEnumerator.
+            IEnumerator IEnumerable.GetEnumerator() => _underlyingInstance.GetEnumerator();
+        }
+    }
+}
diff --git a/src/UnitOfWork/Internals/PredicateConcater.cs b/src/UnitOfWork/Internals/PredicateConcater.cs
new file mode 100644
index 0000000..f2bac43
--- /dev/null
+++ b/src/UnitOfWork/Internals/PredicateConcater.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Linq.Expressions;
+
+namespace Arch.EntityFrameworkCore.UnitOfWork.Internals
+{
+    /// 
+    /// Class PredicateConcater.
+    /// 
+    internal static class PredicateConcater
+    {
+        /// 
+        /// Ors the specified expr2.
+        /// 
+        /// 
+        /// The expr1.
+        /// The expr2.
+        /// Expression<Func<T, System.Boolean>>.
+        public static Expression> Or(this Expression> expr1, Expression> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda>(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        /// 
+        /// Ands the specified expr2.
+        /// 
+        /// 
+        /// The expr1.
+        /// The expr2.
+        /// Expression<Func<T, System.Boolean>>.
+        public static Expression> And(this Expression> expr1, Expression> expr2)
+        {
+            var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
+            return Expression.Lambda>(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
+        }
+
+        /// 
+        /// Replaces the specified search ex.
+        /// 
+        /// The expression.
+        /// The search ex.
+        /// The replace ex.
+        /// Expression.
+        public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx)
+        {
+            return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
+        }
+
+        /// 
+        /// Class ReplaceVisitor.
+        /// Implements the 
+        /// 
+        /// 
+        private class ReplaceVisitor : ExpressionVisitor
+        {
+            /// 
+            /// From
+            /// 
+            private readonly Expression _from, _to;
+
+            /// 
+            /// Initializes a new instance of the  class.
+            /// 
+            /// From.
+            /// To.
+            public ReplaceVisitor(Expression from, Expression to)
+            {
+                _from = from;
+                _to = to;
+            }
+
+            /// 
+            /// Visits the specified node.
+            /// 
+            /// The node.
+            /// Expression.
+            public override Expression Visit(Expression node)
+            {
+                return node == _from ? _to : base.Visit(node);
+            }
+        }
+    }
+}
\ No newline at end of file
From 83d100dd4c247af70b65cd1e02a051cdd7dfddd6 Mon Sep 17 00:00:00 2001
From: Wilson <648178158@qq.com>
Date: Fri, 28 Feb 2020 15:45:41 +0800
Subject: [PATCH 2/2] add predicate
---
 .editorconfig                          |   5 +-
 src/UnitOfWork/PredicateBuilder.cs     | 195 +++++++++++++++++++++++++
 src/UnitOfWork/PredicateWrap.cs        | 178 ++++++++++++++++++++++
 test/UnitOfWork.Tests/TestPredicate.cs |  88 +++++++++++
 4 files changed, 465 insertions(+), 1 deletion(-)
 create mode 100644 src/UnitOfWork/PredicateBuilder.cs
 create mode 100644 src/UnitOfWork/PredicateWrap.cs
 create mode 100644 test/UnitOfWork.Tests/TestPredicate.cs
diff --git a/.editorconfig b/.editorconfig
index efe5c4a..e8730b4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -75,4 +75,7 @@ csharp_new_line_before_else = true
 csharp_new_line_before_catch = true
 csharp_new_line_before_finally = true
 csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_members_in_anonymous_types = true
\ No newline at end of file
+csharp_new_line_before_members_in_anonymous_types = true
+
+# CS1570: XML עͳ XML ʽ
+dotnet_diagnostic.CS1570.severity = none
diff --git a/src/UnitOfWork/PredicateBuilder.cs b/src/UnitOfWork/PredicateBuilder.cs
new file mode 100644
index 0000000..6779165
--- /dev/null
+++ b/src/UnitOfWork/PredicateBuilder.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using Arch.EntityFrameworkCore.UnitOfWork.Collections;
+
+namespace Arch.EntityFrameworkCore.UnitOfWork
+{
+    /// 
+    /// Class PredicateBuilder. This class cannot be inherited.
+    /// 
+    /// 
+    public sealed class PredicateBuilder
+    {
+        /// 
+        /// The instance
+        /// 
+        public static readonly PredicateBuilder Instance = new PredicateBuilder();
+
+        /// 
+        /// Prevents a default instance of the  class from being created.
+        /// 
+        private PredicateBuilder()
+        {
+        }
+
+        /// 
+        /// Customs the specified predicate.
+        /// 
+        /// The predicate.
+        /// PredicateWrap<T>.
+        public PredicateWrap Custom(Expression> predicate) => predicate;
+
+        /// 
+        /// Ins the specified selector.
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The check in.
+        /// PredicateWrap<T>.
+        public PredicateWrap In(Expression> selector, IEnumerable checkIn)
+        {
+            var c = Expression.Constant(checkIn.WrapEnumerable());
+            var p = selector.Parameters[0];
+            var call = Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(TV) }, c, selector.Body);
+            var exp = Expression.Lambda>(call, p);
+            return exp;
+        }
+
+        /// 
+        /// Nots the in.
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The check in.
+        /// PredicateWrap<T>.
+        public PredicateWrap NotIn(Expression> selector, IEnumerable checkIn)
+        {
+            var p = selector.Parameters[0];
+            var values = Expression.Constant(checkIn.WrapEnumerable());
+            var @in = Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(TV) }, values, selector.Body);
+            var not = Expression.Not(@in);
+            var exp = Expression.Lambda>(not, p);
+            return exp;
+        }
+
+        /// 
+        /// Strings the contains.
+        /// 
+        /// The selector.
+        /// The contains.
+        /// PredicateWrap<T>.
+        /// ILL SYSTEM LIB? string.Contains(string) NOT EXIST??
+        public PredicateWrap StringContains(Expression> selector, string contains)
+        {
+            if (string.IsNullOrWhiteSpace(contains)) return null;
+
+            var stringContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) })
+                                       ?? throw new SystemException("ILL SYSTEM LIB? string.Contains(string) NOT EXIST??");
+
+            var parameterExp = selector.Parameters[0];
+            var valExp = Expression.Constant(contains, typeof(string));
+
+            var callExp = Expression.Call(selector.Body, stringContainsMethod, valExp);
+            var lambda = Expression.Lambda>(callExp, parameterExp);
+
+            return lambda;
+        }
+
+        /// 
+        /// Betweens the specified selector.
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The minimum.
+        /// The maximum.
+        /// if set to true [include].
+        /// PredicateWrap<T>.
+        public PredicateWrap Between(Expression> selector, TV min, TV max, bool include = true)
+        {
+            if (null == min && null == max) return null;
+
+            PredicateWrap predicateWrap = null;
+
+            if (null != min)
+            {
+                predicateWrap = include
+                    ? GreaterThanOrEqual(selector, min)
+                    : GreaterThan(selector, min);
+            }
+
+            if (null != max)
+            {
+                predicateWrap &= include
+                    ? LessThanOrEqual(selector, max)
+                    : LessThan(selector, max);
+            }
+
+            return predicateWrap;
+        }
+
+        /// 
+        /// Equals the specified selector.(==)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap Equal(Expression> selector, TV valueToCompare) => BinOp(Expression.Equal, selector, valueToCompare);
+
+        /// 
+        /// Nots the equal.(!=)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap NotEqual(Expression> selector, TV valueToCompare) => BinOp(Expression.NotEqual, selector, valueToCompare);
+
+        /// 
+        /// Lesses the than.(<)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap LessThan(Expression> selector, TV valueToCompare) => BinOp(Expression.LessThan, selector, valueToCompare);
+
+        /// 
+        /// Lesses the than or equal.(<=)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap LessThanOrEqual(Expression> selector, TV valueToCompare) => BinOp(Expression.LessThanOrEqual, selector, valueToCompare);
+
+        /// 
+        /// Greaters the than.(>)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap GreaterThan(Expression> selector, TV valueToCompare) => BinOp(Expression.GreaterThan, selector, valueToCompare);
+
+        /// 
+        /// Greaters the than or equal.(>=)
+        /// 
+        /// The type of the tv.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        public PredicateWrap GreaterThanOrEqual(Expression> selector, TV valueToCompare) => BinOp(Expression.GreaterThanOrEqual, selector, valueToCompare);
+
+        /// 
+        /// Bins the op.
+        /// 
+        /// The type of the tv.
+        /// The op.
+        /// The selector.
+        /// The value to compare.
+        /// PredicateWrap<T>.
+        private static PredicateWrap BinOp(Func op, Expression> selector, TV valueToCompare)
+        {
+            var parameterExp = selector.Parameters[0];
+            var valToCompareExp = Expression.Constant(valueToCompare, typeof(TV));
+
+            var callExp = op(selector.Body, valToCompareExp);
+            var lambda = Expression.Lambda>(callExp, parameterExp);
+
+            return lambda;
+        }
+    }
+}
diff --git a/src/UnitOfWork/PredicateWrap.cs b/src/UnitOfWork/PredicateWrap.cs
new file mode 100644
index 0000000..63bf78a
--- /dev/null
+++ b/src/UnitOfWork/PredicateWrap.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Linq.Expressions;
+using Arch.EntityFrameworkCore.UnitOfWork.Internals;
+
+namespace Arch.EntityFrameworkCore.UnitOfWork
+{
+    /// 
+    /// Class PredicateWrap.
+    /// 
+    public static class PredicateWrap
+    {
+        /// 
+        /// Ops the specified exp.
+        /// 
+        /// 
+        /// The exp.
+        /// PredicateWrap<T>.
+        public static PredicateWrap Op(this Expression> exp) => new PredicateWrap(exp);
+    }
+
+    /// 
+    /// Class PredicateWrap.
+    /// 
+    /// 
+    public class PredicateWrap
+    {
+        /// 
+        /// The expression
+        /// 
+        private readonly Expression> _expression;
+
+        /// 
+        /// Initializes a new instance of the  class.
+        /// 
+        /// The expression.
+        internal PredicateWrap(Expression> expression) => _expression = expression;
+
+        /// 
+        /// Whens the specified condition.
+        /// 
+        /// if set to true [condition].
+        /// PredicateWrap<T>.
+        public PredicateWrap When(bool condition) => condition ? this : null;
+
+        /// 
+        /// Performs an implicit conversion from  to Expression.
+        /// 
+        /// Me.
+        /// The result of the conversion.
+        public static implicit operator Expression>(PredicateWrap me)
+        {
+            var expression = me?._expression;
+            if (true == expression?.CanReduce) return (Expression>)expression.Reduce();
+            return expression;
+        }
+
+        /// 
+        /// Performs an implicit conversion from Expression to .
+        /// 
+        /// Me.
+        /// The result of the conversion.
+        public static implicit operator PredicateWrap(Expression> me) => me == null ? null : new PredicateWrap(me);
+
+        /// 
+        /// Implements the & operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator &(PredicateWrap a, PredicateWrap b)
+        {
+            var aIsNull = null == a?._expression;
+            var bIsNull = null == b?._expression;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a._expression.And(b._expression));
+        }
+
+        /// 
+        /// Implements the & operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator &(PredicateWrap a, Expression> b)
+        {
+            var aIsNull = null == a?._expression;
+            var bIsNull = null == b;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a._expression.And(b));
+        }
+
+        /// 
+        /// Implements the & operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator &(Expression> a, PredicateWrap b)
+        {
+            var aIsNull = null == a;
+            var bIsNull = null == b?._expression;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a.And(b._expression));
+        }
+
+        /// 
+        /// Implements the | operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator |(PredicateWrap a, PredicateWrap b)
+        {
+            var aIsNull = null == a?._expression;
+            var bIsNull = null == b?._expression;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a._expression.Or(b._expression));
+        }
+
+        /// 
+        /// Implements the | operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator |(PredicateWrap a, Expression> b)
+        {
+            var aIsNull = null == a?._expression;
+            var bIsNull = null == b;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a._expression.Or(b));
+        }
+
+        /// 
+        /// Implements the | operator.
+        /// 
+        /// a.
+        /// The b.
+        /// The result of the operator.
+        public static PredicateWrap operator |(Expression> a, PredicateWrap b)
+        {
+            var aIsNull = null == a;
+            var bIsNull = null == b?._expression;
+
+            if (aIsNull && bIsNull) return null;
+
+            if (aIsNull) return b;
+            if (bIsNull) return a;
+
+            return new PredicateWrap(a.Or(b._expression));
+        }
+    }
+}
diff --git a/test/UnitOfWork.Tests/TestPredicate.cs b/test/UnitOfWork.Tests/TestPredicate.cs
new file mode 100644
index 0000000..9f8d523
--- /dev/null
+++ b/test/UnitOfWork.Tests/TestPredicate.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.EntityFrameworkCore;
+using Arch.EntityFrameworkCore.UnitOfWork.Tests.Entities;
+using Xunit;
+
+namespace Arch.EntityFrameworkCore.UnitOfWork.Tests
+{
+    public class TestPredicate
+    {
+        private static readonly InMemoryContext db;
+
+        static TestPredicate()
+        {
+            db = new InMemoryContext();
+            if (db.Countries.Any() == false)
+            {
+                db.AddRange(TestCountries);
+                db.AddRange(TestCities);
+                db.AddRange(TestTowns);
+                db.SaveChanges();
+            }
+        }
+
+
+        [Fact]
+        public async void TestGetFirstOrDefaultAsync()
+        {
+            var repository = new Repository(db);
+            var exp = PredicateWrap.Op(t => t.Name == "A");
+            var city = await repository.GetFirstOrDefaultAsync(predicate: exp);
+            Assert.NotNull(city);
+            Assert.Equal(1, city.Id);
+
+            var predicate = PredicateBuilder.Instance;
+            var exp1 = predicate.Custom(t => t.Id == 3);
+            var city1 = await repository.GetFirstOrDefaultAsync(predicate: exp1);
+            Assert.NotNull(city1);
+            Assert.Equal(3, city1.Id);
+        }
+
+        [Fact]
+        public async void TestGetAll()
+        {
+            var repository = new Repository(db);
+            var predicate = PredicateBuilder.Instance;
+            var exp = predicate.Equal(t => t.Name,"A" );
+            exp &= predicate.Equal(t => t.Id, 3);
+            var cityList = await repository.GetAllAsync(predicate: exp);
+            Assert.Equal(0,cityList.Count);
+
+            
+            exp = predicate.Equal(t => t.Name, "A");
+            exp |= predicate.Equal(t => t.Id, 3);
+            cityList = await repository.GetAllAsync(predicate: exp);
+            Assert.Equal(2,cityList.Count);
+        }
+
+
+        protected static List TestCountries => new List
+        {
+            new Country {Id = 1, Name = "A"},
+            new Country {Id = 2, Name = "B"}
+        };
+
+        public static List TestCities => new List
+        {
+            new City { Id = 1, Name = "A", CountryId = 1},
+            new City { Id = 2, Name = "B", CountryId = 2},
+            new City { Id = 3, Name = "C", CountryId = 1},
+            new City { Id = 4, Name = "D", CountryId = 2},
+            new City { Id = 5, Name = "E", CountryId = 1},
+            new City { Id = 6, Name = "F", CountryId = 2},
+        };
+
+        public static List TestTowns => new List
+        {
+            new Town { Id = 1, Name="TownA", CityId = 1 },
+            new Town { Id = 2, Name="TownB", CityId = 2 },
+            new Town { Id = 3, Name="TownC", CityId = 3 },
+            new Town { Id = 4, Name="TownD", CityId = 4 },
+            new Town { Id = 5, Name="TownE", CityId = 5 },
+            new Town { Id = 6, Name="TownF", CityId = 6 },
+        };
+    }
+}