[英]Include lambda in linq-2-sql expression
我正在尝试重构一些linq-2-sql魔术,但有些事情我显然无法解决。 代码使用此谓词生成器
public static class PredicateBuilder {
public static Expression<Func<T, bool>> True<T>() {
return f => true;
}
public static Expression<Func<T, bool>> False<T>() {
return f => false;
}
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.Cast<Expression>());
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.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
以下列方式使用:
predicate =
predicate.And(
f =>
f.Created != null
? args.Dato1.Date < f.Created.Value.Date &&
f.Created.Value.Date < args.Dato2.Date
: false);
很多。
所以我想也许可以通过以下方式使用更具描述性的名称和更少的行:
private Expression<Func<DAL.Faktura, bool>> beforeInclusiveExpression(Func<DAL.Faktura, DateTime?> getDateTime, DateTime date) {
return f => getDateTime(f).HasValue && getDateTime(f).Value.Date <= date.Date;
}
然后通过以下方式构建谓词:
predicate =
predicate
.And(beforeInclusiveExpression(f => f.Created, d.Dato2)
.And(afterInclusiveExpression(f => f.Created, d.Dato1);
但这不起作用,它仅引发以下错误Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.
。 我知道这是因为linq-2-sql提供程序不知道如何处理lambda,但是我如何将其转换为可以使我重构为更可维护的内容。
为了使beforeInclusiveExpression
方法适用于linq-to-sql
您应该更改参数
Func<DAL.Faktura, DateTime?> getDateTime
至
Expression<Func<DAL.Faktura, DateTime?>> getDateTime
但是,然后您不能简单地调用它,就必须将所有内容转换为Expression
并创建表达式树。
尝试:
private static Expression<Func<DAL.Faktura, bool>> beforeInclusiveExpression(Expression<Func<DAL.Faktura, DateTime?>> getDateTime, DateTime date)
{
// return f => getDateTime(f).HasValue && getDateTime(f).Value.Date <= date.Date;
var parameterF = Expression.Parameter(typeof(DAL.Faktura), "f"); // f
var getDateTimeInvocation = Expression.Invoke(getDateTime, parameterF); // getDateTime(f)
var getDateTime_HasValue = Expression.Property(getDateTimeInvocation, "HasValue"); // getDateTime(f).HasValue
var getDateTime_Value = Expression.Property(getDateTimeInvocation, "Value"); // getDateTime(f).Value
var getDateTime_Value_Date = Expression.Property(getDateTime_Value, "Date"); // getDateTime(f).Value.Date
return Expression.Lambda<Func<DAL.Faktura, bool>>(Expression.AndAlso(getDateTime_HasValue,// getDateTime(f).HasValue &&
Expression.LessThanOrEqual(getDateTime_Value_Date, Expression.Constant(date.Date))), // getDateTime(f).Value.Date <= date.Date
parameterF);
}
经过一段时间的搜寻后,我终于找到了答案。 Dziennys的答案似乎有效,但是依赖Invoke
,而原始方法则无效。 对于表达式和linq-2-sql不太满意,我想使重构尽可能接近原始格式。
向后退一步,我们有一个选择器,一个参数(日期时间)以及两者之间的一个运算符。 这给了我们这个签名
Expression<Func<DAL.Faktura, DateTime?>>, DateTime?, Func<Expression, Expression, BinaryExpression> -> Expression<Func<DAL.Faktura, bool>>
由此,我们必须创建一个新表达式:
private Expression<Func<DAL.Faktura, bool>> dateTimeOperatorExpression(
Expression<Func<DAL.Faktura, DateTime?>> selector, DateTime? date,
Func<Expression, Expression, BinaryExpression> func) {
//We only need the Date part of the DateTime. This lambda does the trick.
var dateSelector = (Expression<Func<DateTime?, DateTime>>) (dt => dt.Value.Date);
//f.Created != null
var dateTimeNotNullPredicate = Expression.NotEqual(selector.Body,
Expression.Constant(null, typeof (DateTime?)));
//This transforms dateSelector: dt => dt.Value.Date
//and selector: f => f.Created
//into a lambda expression: f => f.Created.Value.Date
var swap = new SwapVisitor(dateSelector.Parameters[0], selector.Body);
var selectedPropertyDate = Expression.Lambda<Func<DAL.Faktura, DateTime>>(swap.Visit(dateSelector.Body),
selector.Parameters);
//Apply the supplied operator, here Expression.GreaterThanOrEqual or
//Expression.LessThanOrEqual
var predicate = func(selectedPropertyDate.Body, Expression.Constant(date.Value.Date, typeof (DateTime)));
var combined = Expression.And(dateTimeNotNullPredicate, predicate);
return Expression.Lambda<Func<DAL.Faktura, bool>>(combined, selector.Parameters);
}
ExpressionVisitor
帮助程序,不确定确切的作者是谁,但是我在SO上找到了它。
class SwapVisitor : ExpressionVisitor {
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) {
return node == from ? to : base.Visit(node);
}
}
因此,原始代码
predicate.And(
f =>
f.Created != null
? d.Dato1.Date < f.Created.Value.Date &&
f.Created.Value.Date < d.Dato2.Date
: false);
创建一个大致如下所示的表达式:
expr2
{ f =>
if (f.Created != Convert(null))
(
(value(MyType+<>c__DisplayClass36).d.Dato1.Date <= f.Created.Value.Date)
AndAlso
(f.Created.Value.Date <= value(MyType+<>c__DisplayClass36).d.Dato2.Date)
)
else
(False)
} System.Linq.Expressions.Expression<System.Func<DAL.Faktura,bool>>
而上面的代码和这个方法
private Expression<Func<DAL.Faktura, bool>> betweenInclusiveExpression(
Expression<Func<DAL.Faktura, DateTime?>> selector, DateTime? beginingDateTime, DateTime? endDateTime) {
var afterPredicate = dateTimeOperatorExpression(selector, beginingDateTime, Expression.GreaterThanOrEqual);
var beforePredicate = dateTimeOperatorExpression(selector, endDateTime, Expression.LessThanOrEqual);
var combined = Expression.AndAlso(afterPredicate.Body, beforePredicate.Body);
return Expression.Lambda<Func<DAL.Faktura, bool>>(combined, selector.Parameters);
}
产生这个表达式:
expr2
{
f =>
(
((f.Created != null) AndAlso (f.Created.Value.Date >= 07-07-2015 00:00:00))
AndAlso
((f.Created != null) AndAlso (f.Created.Value.Date <= 07-07-2015 00:00:00))
)
} System.Linq.Expressions.Expression<System.Func<DAL.Faktura,bool>>
我唯一不确定的是,固定日期与原始日期是否不同。 我认为,一旦linq-2-sql提供程序将表达式转换为SQL,它就会捕获变量。 换句话说,现在发生的情况稍微好一些。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.