Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PredicateWrap #131

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
csharp_new_line_before_members_in_anonymous_types = true

# CS1570: XML ע�ͳ��� XML ��ʽ����
dotnet_diagnostic.CS1570.severity = none
54 changes: 54 additions & 0 deletions src/UnitOfWork/Collections/EnumerableExtensionMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Arch.EntityFrameworkCore.UnitOfWork.Collections
{
/// <summary>
/// Class EnumerableExtensionMethod.
/// </summary>
public static class EnumerableExtensionMethod
{
/// <summary>
/// 包裹泛型IEnumerable实例, 避免可能的数组实例造成EF动态过滤失败
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <returns>IEnumerable&lt;T&gt;.</returns>
public static IEnumerable<T> WrapEnumerable<T>(this IEnumerable<T> source) => new EnumerableWrapper<T>(source);

/// <summary>
/// Class EnumerableWrapper.
/// Implements the <see cref="System.Collections.Generic.IEnumerable{T}" />
/// </summary>
/// <typeparam name="T"></typeparam>
/// <seealso cref="System.Collections.Generic.IEnumerable{T}" />
private class EnumerableWrapper<T> : IEnumerable<T>
{
/// <summary>
/// The underlying instance
/// </summary>
private readonly IEnumerable<T> _underlyingInstance;

/// <summary>
/// Initializes a new instance of the <see cref="EnumerableWrapper{T}"/> class.
/// </summary>
/// <param name="underlyingInstance">The underlying instance.</param>
public EnumerableWrapper(IEnumerable<T> underlyingInstance) => _underlyingInstance = underlyingInstance;

/// <summary>
/// 返回一个循环访问集合的枚举器。
/// </summary>
/// <returns>可用于循环访问集合的 <see cref="T:System.Collections.Generic.IEnumerator`1" />。</returns>
IEnumerator<T> IEnumerable<T>.GetEnumerator() => _underlyingInstance.GetEnumerator();

/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns>IEnumerator.</returns>
IEnumerator IEnumerable.GetEnumerator() => _underlyingInstance.GetEnumerator();
}
}
}
83 changes: 83 additions & 0 deletions src/UnitOfWork/Internals/PredicateConcater.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Linq.Expressions;

namespace Arch.EntityFrameworkCore.UnitOfWork.Internals
{
/// <summary>
/// Class PredicateConcater.
/// </summary>
internal static class PredicateConcater
{
/// <summary>
/// Ors the specified expr2.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr1">The expr1.</param>
/// <param name="expr2">The expr2.</param>
/// <returns>Expression&lt;Func&lt;T, System.Boolean&gt;&gt;.</returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}

/// <summary>
/// Ands the specified expr2.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr1">The expr1.</param>
/// <param name="expr2">The expr2.</param>
/// <returns>Expression&lt;Func&lt;T, System.Boolean&gt;&gt;.</returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}

/// <summary>
/// Replaces the specified search ex.
/// </summary>
/// <param name="expression">The expression.</param>
/// <param name="searchEx">The search ex.</param>
/// <param name="replaceEx">The replace ex.</param>
/// <returns>Expression.</returns>
public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

/// <summary>
/// Class ReplaceVisitor.
/// Implements the <see cref="System.Linq.Expressions.ExpressionVisitor" />
/// </summary>
/// <seealso cref="System.Linq.Expressions.ExpressionVisitor" />
private class ReplaceVisitor : ExpressionVisitor
{
/// <summary>
/// From
/// </summary>
private readonly Expression _from, _to;

/// <summary>
/// Initializes a new instance of the <see cref="ReplaceVisitor"/> class.
/// </summary>
/// <param name="from">From.</param>
/// <param name="to">To.</param>
public ReplaceVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}

/// <summary>
/// Visits the specified node.
/// </summary>
/// <param name="node">The node.</param>
/// <returns>Expression.</returns>
public override Expression Visit(Expression node)
{
return node == _from ? _to : base.Visit(node);
}
}
}
}
195 changes: 195 additions & 0 deletions src/UnitOfWork/PredicateBuilder.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Class PredicateBuilder. This class cannot be inherited.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class PredicateBuilder<T>
{
/// <summary>
/// The instance
/// </summary>
public static readonly PredicateBuilder<T> Instance = new PredicateBuilder<T>();

/// <summary>
/// Prevents a default instance of the <see cref="PredicateBuilder{T}"/> class from being created.
/// </summary>
private PredicateBuilder()
{
}

/// <summary>
/// Customs the specified predicate.
/// </summary>
/// <param name="predicate">The predicate.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> Custom(Expression<Func<T, bool>> predicate) => predicate;

/// <summary>
/// Ins the specified selector.
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="checkIn">The check in.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> In<TV>(Expression<Func<T, TV>> selector, IEnumerable<TV> 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<Func<T, bool>>(call, p);
return exp;
}

/// <summary>
/// Nots the in.
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="checkIn">The check in.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> NotIn<TV>(Expression<Func<T, TV>> selector, IEnumerable<TV> 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<Func<T, bool>>(not, p);
return exp;
}

/// <summary>
/// Strings the contains.
/// </summary>
/// <param name="selector">The selector.</param>
/// <param name="contains">The contains.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
/// <exception cref="SystemException">ILL SYSTEM LIB? string.Contains(string) NOT EXIST??</exception>
public PredicateWrap<T> StringContains(Expression<Func<T, string>> 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<Func<T, bool>>(callExp, parameterExp);

return lambda;
}

/// <summary>
/// Betweens the specified selector.
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <param name="include">if set to <c>true</c> [include].</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> Between<TV>(Expression<Func<T, TV>> selector, TV min, TV max, bool include = true)
{
if (null == min && null == max) return null;

PredicateWrap<T> 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;
}

/// <summary>
/// Equals the specified selector.(==)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> Equal<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.Equal, selector, valueToCompare);

/// <summary>
/// Nots the equal.(!=)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> NotEqual<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.NotEqual, selector, valueToCompare);

/// <summary>
/// Lesses the than.(<)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> LessThan<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.LessThan, selector, valueToCompare);

/// <summary>
/// Lesses the than or equal.(<=)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> LessThanOrEqual<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.LessThanOrEqual, selector, valueToCompare);

/// <summary>
/// Greaters the than.(>)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> GreaterThan<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.GreaterThan, selector, valueToCompare);

/// <summary>
/// Greaters the than or equal.(>=)
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
public PredicateWrap<T> GreaterThanOrEqual<TV>(Expression<Func<T, TV>> selector, TV valueToCompare) => BinOp(Expression.GreaterThanOrEqual, selector, valueToCompare);

/// <summary>
/// Bins the op.
/// </summary>
/// <typeparam name="TV">The type of the tv.</typeparam>
/// <param name="op">The op.</param>
/// <param name="selector">The selector.</param>
/// <param name="valueToCompare">The value to compare.</param>
/// <returns>PredicateWrap&lt;T&gt;.</returns>
private static PredicateWrap<T> BinOp<TV>(Func<Expression, Expression, Expression> op, Expression<Func<T, TV>> 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<Func<T, bool>>(callExp, parameterExp);

return lambda;
}
}
}
Loading