You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Queries/Builder/MemoryStorageQueryBuilder.cs

137 lines
5.3 KiB

6 years ago
using System;
using System.Linq;
using System.Linq.Expressions;
using ZeroLevel.Services.ObjectMapping;
using ZeroLevel.Services.Reflection;
using ZeroLevel.Specification;
namespace ZeroLevel.Patterns.Queries
{
internal sealed class MemoryQuery<T>
: IRealQuery<T, Func<T, bool>>
{
public MemoryQuery(Func<T, bool> predicate)
{
this.Query = predicate;
}
public Func<T, bool> Query { get; }
}
internal sealed class MemoryStorageQueryBuilder<T> :
IQueryBuilder<T, Func<T, bool>>
{
public IRealQuery<T, Func<T, bool>> Build(IQuery query)
{
var exp = ResolveQuery(query);
return new MemoryQuery<T>(exp.Compile());
}
private static Expression<Func<T, bool>> ResolveQuery(IQuery query)
{
if (query is AndQuery)
{
return ResolveQueryAnd(query as AndQuery);
}
else if (query is OrQuery)
{
return ResolveQueryOr(query as OrQuery);
}
else if (query is NotQuery)
{
return ResolveQueryNot(query as NotQuery);
}
return ResolveQueryOp(query as QueryOp);
}
private static Expression<Func<T, bool>> ResolveQueryOp(QueryOp query)
{
if (query.Operation == QueryOperation.ALL)
{
return Expression.Lambda<Func<T, bool>>(Expression.Constant(true, typeof(bool)), new[] { Expression.Parameter(typeof(object)) });
}
var mapper = TypeMapper.Create<T>(true);
var argument = Expression.Parameter(typeof(T));
Expression param;
if (mapper[query.FieldName].IsField)
{
param = Expression.Field(argument, query.FieldName);
}
else
{
param = Expression.Property(argument, query.FieldName);
}
object value;
Expression constant;
if (query.Operation == QueryOperation.IN)
{
if (TypeHelpers.IsArray(mapper[query.FieldName].ClrType.GetElementType()) ||
TypeHelpers.IsEnumerable(mapper[query.FieldName].ClrType.GetElementType()))
{
value = Convert.ChangeType(query.Value, mapper[query.FieldName].ClrType.GetElementType());
constant = Expression.Constant(value, mapper[query.FieldName].ClrType.GetElementType());
}
else
{
value = query.Value;
constant = Expression.Constant(value, mapper[query.FieldName].ClrType);
}
}
else
{
value = Convert.ChangeType(query.Value, mapper[query.FieldName].ClrType);
constant = Expression.Constant(value, mapper[query.FieldName].ClrType);
}
switch (query.Operation)
{
case QueryOperation.EQ:
return Expression.Lambda<Func<T, bool>>(Expression.Equal(param, constant),
new[] { argument });
case QueryOperation.GT:
return Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(param, constant),
new[] { argument });
case QueryOperation.GTE:
return Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(param, constant),
new[] { argument });
case QueryOperation.LT:
return Expression.Lambda<Func<T, bool>>(Expression.LessThan(param, constant),
new[] { argument });
case QueryOperation.LTE:
return Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(param, constant),
new[] { argument });
case QueryOperation.NEQ:
return Expression.Lambda<Func<T, bool>>(Expression.NotEqual(param, constant),
new[] { argument });
case QueryOperation.IN:
var overload = typeof(Enumerable).GetMethods()
.Single(mi => mi.Name.Equals("Contains", StringComparison.Ordinal) && mi.GetParameters().Count() == 2);
var call = Expression.Call(
overload.MakeGenericMethod(mapper[query.FieldName].ClrType.GetElementType()),
param, constant);
return Expression.Lambda<Func<T, bool>>(call, new[] { argument });
}
return null;
}
private static Expression<Func<T, bool>> ResolveQueryAnd(AndQuery query)
{
var left = ResolveQuery(query.Left);
var right = ResolveQuery(query.Right);
return PredicateBuilder.And(left, right);
}
private static Expression<Func<T, bool>> ResolveQueryOr(OrQuery query)
{
var left = ResolveQuery(query.Left);
var right = ResolveQuery(query.Right);
return PredicateBuilder.Or(left, right);
}
private static Expression<Func<T, bool>> ResolveQueryNot(NotQuery query)
{
return PredicateBuilder.Not(ResolveQuery(query.Query));
}
}
}

Powered by TurnKey Linux.