[英]Dapper IPredicate to linq conversion
在我的應用程序中,有一些實現一個通用接口的類,我們稱之為IValidator。 實現此類接口的每個類都將返回PredicateGroup對象。 因此,出於測試目的,我決定從數據庫的特定視圖中獲取所有數據,然后在返回的集合(即IEnumerable)上通過linq快速過濾(其中不對具有不同謂詞的數據庫進行多次調用)。
dapper是否支持從IPredicate / PredicateGroup到Func <>的這種轉換,或者還有另一個更快/更好的解決方案?
這是我想要實現的一些演示:
IEnumerable<Products> products = null;
using (var cn = new SqlConnection("connectionstring"))
{
//Get all elemnts from database(using only one call)
products = cn.GetList<Products>(Predicates.Field<Products>(f => f.Discontinued, Operator.Eq, true));
}
// class which implement IValidator and returns predicate group
List<IPredicate> computerPredicates = new List<IPredicate>
{
Predicates.Field<Products>(f => f.ProductName, Operator.Eq, "Computer"),
Predicates.Field<Products>(f => f.Price, Operator.Eq, 1200)
};
var computerPredicatesGroup = new PredicateGroup {Predicates = computerPredicates };
// class which implement IValidator and returns predicate group
List<IPredicate> phonePredicates = new List<IPredicate>
{
Predicates.Field<Products>(f => f.ProductName, Operator.Eq, "Phone"),
Predicates.Field<Products>(f => f.Price, Operator.Eq, 400)
};
var phonePredicatesGroup = new PredicateGroup { Predicates = phonePredicates };
var computers = products.Where( /* computerPredicates */); //??
var phones = products.Where( /* phonePredicatesGroup */); //??
我沒有找到任何解決方案,所以我決定編寫自己的解析器。
示例如何使用它:
測試模型:
class TestCarModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Model { get; set; }
public string Type { get; set; }
public float Cost { get; set; }
}
private List<TestCarModel> PrepareTestData()
{
return new List<TestCarModel>()
{
new TestCarModel(){ Id = 1, Name = "Fiat", Model = "500", Type = "Kompakt", Cost = 20000 },
new TestCarModel(){ Id = 2, Name = "Fiat", Model = "Bravo", Type = "Kompakt", Cost = 30000 },
new TestCarModel(){ Id = 3, Name = "Opel", Model = "Astra", Type = "Sedan", Cost = 20000 },
new TestCarModel(){ Id = 4, Name = "Honda", Model = "Civic", Type = "Hatchback", Cost = 15000 },
new TestCarModel(){ Id = 5, Name = "Audi", Model = "A4", Type = "Sedan", Cost = 40000 },
};
}
范例1:
var groupPredicate = new PredicateGroup
{
Operator = GroupOperator.Or,
Predicates = new List<IPredicate>()
};
var predicateGroup1 = new PredicateGroup
{
Operator = GroupOperator.Or,
Predicates = new List<IPredicate>()
{
Predicates.Field<TestCarModel>(f => f.Model, Operator.Eq, "500"),
Predicates.Field<TestCarModel>(f => f.Model, Operator.Eq, "Bravo")
}
};
var predicateGroup2 = new PredicateGroup
{
Operator = GroupOperator.Or,
Predicates = new List<IPredicate>()
{
Predicates.Field<TestCarModel>(f => f.Name, Operator.Eq, "Opel"),
Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Hatchback"),
}
};
groupPredicate.Predicates.Add(Predicates.Field<TestCarModel>(f => f.Cost, Operator.Gt, 35000));
groupPredicate.Predicates.Add(predicateGroup1);
groupPredicate.Predicates.Add(predicateGroup2);
var testdata = PrepareTestData();
var expression = PredicateParser<TestCarModel>.Parse(groupPredicate).Compile();
var result = testdata.Where(expression);
范例2:
var predicates = new List<IPredicate>
{
Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Sedan"),
Predicates.Field<TestCarModel>(f => f.Type, Operator.Eq, "Hatchback"),
};
var testdata = PrepareTestData();
var expression = PredicateParser<TestCarModel>.ParseOr(predicates).Compile();
var result = testdata.Where(expression);
碼:
謂詞分析器
public static class PredicateParser<T>
{
public static Expression<Func<T, bool>> Parse(PredicateGroup predicateGroup)
{
var compositeIterator = new CompositeIterator<T>();
var result = compositeIterator.Prepare(predicateGroup);
return result;
}
public static Expression<Func<T, bool>> ParseAnd(IList<IPredicate> fieldPredicates)
{
Expression<Func<T, bool>> finalQuery = t => true;
foreach (var predicate in fieldPredicates)
{
finalQuery = finalQuery.And(Parse(predicate));
}
return finalQuery;
}
public static Expression<Func<T, bool>> ParseOr(IList<IPredicate> fieldPredicates)
{
Expression<Func<T, bool>> finalQuery = t => false;
foreach (var predicate in fieldPredicates)
{
finalQuery = finalQuery.Or(Parse(predicate));
}
return finalQuery;
}
public static Expression<Func<T, bool>> Parse(IPredicate predicate)
{
IFieldPredicate fieldPredicate = (IFieldPredicate)predicate;
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
MemberExpression memberExpression = Expression.PropertyOrField(parameterExpression, fieldPredicate.PropertyName);
UnaryExpression propertyValue = Expression.Convert(Expression.Constant(fieldPredicate.Value), memberExpression.Type);
var operatorMatrix = new Dictionary<KeyValuePair<Operator, bool>, Func<Expression>>
{
{ new KeyValuePair<Operator, bool>(Operator.Like, false), () => LikeExpression.Like(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Eq, false), () => Expression.Equal(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Gt, false), () => Expression.GreaterThan(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Ge, false), () => Expression.GreaterThanOrEqual(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Lt, false), () => Expression.LessThan(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Le, false), () => Expression.LessThanOrEqual(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Like, true), () => LikeExpression.NotLike(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Eq, true), () => Expression.NotEqual(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Gt, true), () => Expression.LessThan(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Ge, true), () => Expression.LessThanOrEqual(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Lt, true), () => Expression.GreaterThan(memberExpression, propertyValue) },
{ new KeyValuePair<Operator, bool>(Operator.Le, true), () => Expression.GreaterThanOrEqual(memberExpression, propertyValue) },
};
var body = operatorMatrix[new KeyValuePair<Operator, bool>(fieldPredicate.Operator, fieldPredicate.Not)].Invoke();
return Expression.Lambda<Func<T, bool>>(body, parameterExpression);
}
}
謂詞生成器
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
LikeExpression
public static class LikeExpression
{
private static readonly MethodInfo ApplyLikeMethodInfo = typeof(LikeExpression).GetMethod("ApplyLike");
private static readonly MethodInfo ApplyLikeNoCaseMethodInfo = typeof(LikeExpression).GetMethod("ApplyLikeNoCase");
private static readonly MethodInfo ApplyNotLikeMethodInfo = typeof(LikeExpression).GetMethod("ApplyNotLike");
private static readonly MethodInfo ApplyNotLikeNoCaseMethodInfo = typeof(LikeExpression).GetMethod("ApplyNotLikeNoCase");
public static Expression Like(Expression lhs, Expression pattern, bool caseSensitive = false)
{
return caseSensitive
? Expression.Call(ApplyLikeMethodInfo, lhs, pattern)
: Expression.Call(ApplyLikeNoCaseMethodInfo, lhs, pattern);
}
public static Expression NotLike(Expression lhs, Expression pattern, bool caseSensitive = false)
{
return caseSensitive
? Expression.Call(ApplyNotLikeMethodInfo, lhs, pattern)
: Expression.Call(ApplyNotLikeNoCaseMethodInfo, lhs, pattern);
}
public static bool ApplyLike(string text, string likePattern)
{
string pattern = PatternToRegex(likePattern);
return Regex.IsMatch(text, pattern, RegexOptions.None);
}
public static bool ApplyLikeNoCase(string text, string likePattern)
{
string pattern = PatternToRegex(likePattern);
return Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
}
public static bool ApplyNotLike(string text, string likePattern)
{
string pattern = PatternToRegex(likePattern);
return !Regex.IsMatch(text, pattern, RegexOptions.None);
}
public static bool ApplyNotLikeNoCase(string text, string likePattern)
{
string pattern = PatternToRegex(likePattern);
return !Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
}
public static string PatternToRegex(string pattern)
{
pattern = Regex.Escape(pattern);
pattern = pattern.Replace("%", @".*");
pattern = $"^{pattern}$";
return pattern;
}
}
CompositeIterator
public class CompositeIterator<T>
{
private Expression<Func<T, bool>> finalQuery;
private Expression<Func<T, bool>> higherQuery;
private GroupOperator? previousOperator;
private int level = -1;
public Expression<Func<T, bool>> Prepare(PredicateGroup predicateGroup)
{
previousOperator = predicateGroup.Operator;
finalQuery = t => predicateGroup.Operator == GroupOperator.And;
CallRecursive(predicateGroup);
return finalQuery;
}
private void CallRecursive(PredicateGroup predicateGroup)
{
var nodes = predicateGroup.Predicates;
bool isSet = true;
++level;
foreach (var n in nodes)
{
if (n is PredicateGroup @group)
{
CallRecursive(@group);
--level;
}
else
{
var expr = PredicateParser<T>.Parse((IFieldPredicate)n);
if (level > 0)
{
if (isSet)
{
higherQuery = t => predicateGroup.Operator == GroupOperator.And;
isSet = false;
}
higherQuery = predicateGroup.Operator == GroupOperator.And
? higherQuery.And(expr)
: higherQuery.Or(expr);
}
else
{
previousOperator = predicateGroup.Operator;
finalQuery = predicateGroup.Operator == GroupOperator.And
? finalQuery.And(expr)
: finalQuery.Or(expr);
}
}
}
if (higherQuery != null)
{
finalQuery = previousOperator == GroupOperator.And
? finalQuery.And(higherQuery)
: finalQuery.Or(higherQuery);
}
higherQuery = null;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.