Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
Merge pull request #14 from osoykan/dev
Browse files Browse the repository at this point in the history
dev to master
  • Loading branch information
osoykan authored Feb 10, 2017
2 parents d106919 + fd29a5e commit 77cc8e8
Show file tree
Hide file tree
Showing 56 changed files with 1,367 additions and 1,470 deletions.
4 changes: 2 additions & 2 deletions src/Stove.Dapper/Dapper/DapperRepositoryRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static void RegisterRepositories(Type dbContextType, IIocBuilder builder)
? autoRepositoryAttr.RepositoryImplementation.MakeGenericType(entityTypeInfo.EntityType)
: autoRepositoryAttr.RepositoryImplementation.MakeGenericType(entityTypeInfo.DeclaringType, entityTypeInfo.EntityType);

builder.RegisterServices(r => r.UseBuilder(cb => cb.RegisterType(implType).As(genericRepositoryType).AsImplementedInterfaces()));
builder.RegisterServices(r => r.UseBuilder(cb => cb.RegisterType(implType).As(genericRepositoryType).AsImplementedInterfaces().InjectPropertiesAsAutowired()));
}
else
{
Expand All @@ -38,7 +38,7 @@ public static void RegisterRepositories(Type dbContextType, IIocBuilder builder)
? autoRepositoryAttr.RepositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.EntityType, primaryKeyType)
: autoRepositoryAttr.RepositoryImplementationWithPrimaryKey.MakeGenericType(entityTypeInfo.DeclaringType, entityTypeInfo.EntityType, primaryKeyType);

builder.RegisterServices(r => r.UseBuilder(cb => cb.RegisterType(implType).As(genericRepositoryTypeWithPrimaryKey).AsImplementedInterfaces()));
builder.RegisterServices(r => r.UseBuilder(cb => cb.RegisterType(implType).As(genericRepositoryTypeWithPrimaryKey).AsImplementedInterfaces().InjectPropertiesAsAutowired()));
}
}
}
Expand Down
119 changes: 119 additions & 0 deletions src/Stove.Dapper/Dapper/Expressions/DapperEvaluator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Stove.Dapper.Dapper.Expressions
{
/// <summary>
/// Reference
/// From:http://blogs.msdn.com/b/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx
/// </summary>
internal class Evaluator
{
public static Expression PartialEval(Expression exp, Func<Expression, bool> canBeEval)
{
return new SubtreeEvaluator(new Nominator(canBeEval).Nominate(exp)).Eval(exp);
}

public static Expression PartialEval(Expression exp)
{
return PartialEval(exp, CanBeEvaluatedLocally);
}

private static bool CanBeEvaluatedLocally(Expression exp)
{
return exp.NodeType != ExpressionType.Parameter;
}

private class SubtreeEvaluator : ExpressionVisitor
{
private readonly HashSet<Expression> _candidates;

internal SubtreeEvaluator(HashSet<Expression> candidates)
{
_candidates = candidates;
}

internal Expression Eval(Expression exp)
{
return Visit(exp);
}

public override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}

if (_candidates.Contains(exp))
{
return Evaluate(exp);
}

return base.Visit(exp);
}

private Expression Evaluate(Expression exp)
{
if (exp.NodeType == ExpressionType.Constant)
{
return exp;
}

LambdaExpression lambda = Expression.Lambda(exp);
Delegate del = lambda.Compile();

return Expression.Constant(del.DynamicInvoke(null), exp.Type);
}
}

private class Nominator : ExpressionVisitor
{
private readonly Func<Expression, bool> _canBeEval;
private HashSet<Expression> _candidates;
private bool _cannotBeEval;

internal Nominator(Func<Expression, bool> canBeEval)
{
_canBeEval = canBeEval;
}

internal HashSet<Expression> Nominate(Expression exp)
{
_candidates = new HashSet<Expression>();
Visit(exp);
return _candidates;
}

public override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}

bool saveCannotBeEval = _cannotBeEval;
_cannotBeEval = false;

base.Visit(exp);

if (!_cannotBeEval)
{
if (_canBeEval(exp))
{
_candidates.Add(exp);
}
else
{
_cannotBeEval = true;
}
}

_cannotBeEval |= saveCannotBeEval;

return exp;
}
}
}
}
31 changes: 31 additions & 0 deletions src/Stove.Dapper/Dapper/Expressions/DapperExpressionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Linq.Expressions;

using DapperExtensions;

using JetBrains.Annotations;

using Stove.Domain.Entities;

namespace Stove.Dapper.Dapper.Expressions
{
/// <summary>
/// http://stackoverflow.com/questions/15154783/pulling-apart-expressionfunct-object
/// http://stackoverflow.com/questions/16083895/grouping-lambda-expressions-by-operators-and-using-them-with-dapperextensions-p
/// http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
/// http://msdn.microsoft.com/en-us/library/bb546136(v=vs.110).aspx
/// http://stackoverflow.com/questions/14437239/change-a-linq-expression-predicate-from-one-type-to-another/14439071#14439071
/// </summary>
internal static class DapperExpressionExtensions
{
public static IPredicate ToPredicateGroup<TEntity, TPrimaryKey>([NotNull] this Expression<Func<TEntity, bool>> expression) where TEntity : class, IEntity<TPrimaryKey>
{
Check.NotNull(expression, nameof(expression));

var dev = new DapperExpressionVisitor<TEntity, TPrimaryKey>();
IPredicate pg = dev.Process(expression);

return pg;
}
}
}
222 changes: 222 additions & 0 deletions src/Stove.Dapper/Dapper/Expressions/DapperExpressionVisitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using DapperExtensions;

using Stove.Domain.Entities;

namespace Stove.Dapper.Dapper.Expressions
{
/// <summary>
/// This class converts an Expression{Func{TEntity, bool}} into an IPredicate group that can be used with
/// DapperExtension's predicate system
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TPrimaryKey">The type of the primary key.</typeparam>
/// <seealso cref="System.Linq.Expressions.ExpressionVisitor" />
internal class DapperExpressionVisitor<TEntity, TPrimaryKey> : ExpressionVisitor where TEntity : class, IEntity<TPrimaryKey>
{
private PredicateGroup _pg;
private Expression _processedProperty;
private bool _unarySpecified;

public DapperExpressionVisitor()
{
Expressions = new HashSet<Expression>();
}

/// <summary>
/// Holds BinaryExpressions
/// </summary>
public HashSet<Expression> Expressions { get; }

public IPredicate Process(Expression exp)
{
_pg = new PredicateGroup { Predicates = new List<IPredicate>() };
Visit(Evaluator.PartialEval(exp));

// the 1st expression determines root group operator
if (Expressions.Any())
{
_pg.Operator = Expressions.First().NodeType == ExpressionType.OrElse ? GroupOperator.Or : GroupOperator.And;
}

return _pg.Predicates.Count == 1 ? _pg.Predicates[0] : _pg;
}

private static PredicateGroup GetLastPredicateGroup(PredicateGroup grp)
{
IList<IPredicate> groups = grp.Predicates;

if (!groups.Any())
{
return grp;
}

IPredicate last = groups.Last();

if (last is PredicateGroup)
{
return GetLastPredicateGroup(last as PredicateGroup);
}

return grp;
}

private IFieldPredicate GetLastField()
{
PredicateGroup lastGrp = GetLastPredicateGroup(_pg);

IPredicate last = lastGrp.Predicates.Last();

return last as IFieldPredicate;
}

private static Operator DetermineOperator(Expression binaryExpression)
{
switch (binaryExpression.NodeType)
{
case ExpressionType.Equal:
return Operator.Eq;
case ExpressionType.GreaterThan:
return Operator.Gt;
case ExpressionType.GreaterThanOrEqual:
return Operator.Ge;
case ExpressionType.LessThan:
return Operator.Lt;
case ExpressionType.LessThanOrEqual:
return Operator.Le;
default:
return Operator.Eq;
}
}

private void AddField(MemberExpression exp, Operator op = Operator.Eq, object value = null, bool not = false)
{
PredicateGroup pg = GetLastPredicateGroup(_pg);

// need convert from Expression<Func<T, bool>> to Expression<Func<T, object>> as this is what Predicates.Field() requires
Expression<Func<TEntity, object>> fieldExp = Expression.Lambda<Func<TEntity, object>>(Expression.Convert(exp, typeof(object)), exp.Expression as ParameterExpression);

IFieldPredicate field = Predicates.Field(fieldExp, op, value, not);
pg.Predicates.Add(field);
}

protected override Expression VisitBinary(BinaryExpression node)
{
Expressions.Add(node);

ExpressionType nt = node.NodeType;

if (nt == ExpressionType.OrElse || nt == ExpressionType.AndAlso)
{
var pg = new PredicateGroup
{
Predicates = new List<IPredicate>(),
Operator = nt == ExpressionType.OrElse ? GroupOperator.Or : GroupOperator.And
};

_pg.Predicates.Add(pg);
}

Visit(node.Left);

if (node.Left is MemberExpression)
{
IFieldPredicate field = GetLastField();
field.Operator = DetermineOperator(node);

if (nt == ExpressionType.NotEqual)
{
field.Not = true;
}
}

Visit(node.Right);

return node;
}

protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType != MemberTypes.Property || node.Expression.Type != typeof(TEntity))
{
throw new NotSupportedException($"The member '{node}' is not supported");
}

// skip if prop is part of a VisitMethodCall
if (_processedProperty != null && _processedProperty == node)
{
_processedProperty = null;
return node;
}

AddField(node);

return node;
}

protected override Expression VisitConstant(ConstantExpression node)
{
IFieldPredicate field = GetLastField();
field.Value = node.Value;

return node;
}

protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Type == typeof(bool) && node.Method.DeclaringType == typeof(string))
{
object arg = ((ConstantExpression)node.Arguments[0]).Value;
var op = Operator.Like;

switch (node.Method.Name.ToLowerInvariant())
{
case "startswith":
arg = arg + "%";
break;
case "endswith":
arg = "%" + arg;
break;
case "contains":
arg = "%" + arg + "%";
break;
case "equals":
op = Operator.Eq;
break;
default:
throw new NotSupportedException($"The method '{node}' is not supported");
}

// this is a PropertyExpression but as it's internal, to use, we cast to the base MemberExpression instead (see http://social.msdn.microsoft.com/Forums/en-US/ab528f6a-a60e-4af6-bf31-d58e3f373356/resolving-propertyexpressions-and-fieldexpressions-in-a-custom-linq-provider)
_processedProperty = node.Object;
var me = _processedProperty as MemberExpression;

AddField(me, op, arg, _unarySpecified);

// reset if applicable
_unarySpecified = false;

return node;
}

throw new NotSupportedException($"The method '{node}' is not supported");
}

protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType != ExpressionType.Not)
{
throw new NotSupportedException($"The unary operator '{node.NodeType}' is not supported");
}

_unarySpecified = true;

return base.VisitUnary(node); // returning base because we want to continue further processing - ie subsequent call to VisitMethodCall
}
}
}
Loading

0 comments on commit 77cc8e8

Please sign in to comment.