如何修改Expression <Func >的类型参数?
我有以下实例:
Expression<Func>
我希望将其转换为以下实例,因此它可用于在Entity Framework中运行查询:
Expression<Func>
这将允许我对任何实现IRequiredDate的Model使用通用过滤查询,例如:
// In some repository function: var query = DbContext.Set() .FilterByDateRange(DateTime.Today, DateTime.Today); var query = DbContext.Set() .FilterByDateRange(DateTime.Today, DateTime.Today); var query = DbContext.Set() .FilterByDateRange(DateTime.Today, DateTime.Today); // The general purpose function, can filter for any model implementing IRequiredDate public static IQueryable FilterByDate(IQueryable query, DateTime startDate, DateTime endDate) where TModel : IRequiredDate { // This will NOT WORK, as E/F won't accept an expression of type IRequiredDate, even though TModel implements IRequiredDate // Expression<Func> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate; // query = query.Where(dateRangeFilter); // This also WON'T WORK, x.Date is compiled into the expression as a member of IRequiredDate instead of TModel, so E/F knocks it back for the same reason: // Expression<Func> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate; // query = query.Where(dateRangeFilter); // All you need is lov.... uh... something like this: Expression<Func> dateRangeFilter = x => x.Date >= startDate && x.Date <= endDate; Expression<Func> dateRangeFilterForType = ConvertExpressionType(dateRangeFilter); // Must convert the expression from one type to another query = query.Where(dateRangeFilterForType) // Ahhhh. this will work. return query; } public static ConvertExpressionType(Expression<Func> expression) where TModel : TInterface // It must implement the interface, since we're about to translate them { Expression<Func> newExpression = null; // TODO: How to convert the contents of expression into newExpression, modifying the // generic type parameter along the way?? return newExpression; }
我知道他们是不同的类型,不能演员。 但是我想知道是否有办法创建一个新的Expression<Func>
,然后根据提供的Expression<Func>
的内容重建它,将任何类型引用从IRequiredDate
切换到TModel
在这个过程中。
可以这样做吗?
所以实际进行映射的方法并不那么难,但遗憾的是,我没有一种很好的方法可以看出它的推广。 这是一个采用Func
并将其映射到委托的方法,其中参数是比T1
更多的派生:
public static Expression> Foo( Expression> expression) where NewParam : OldParam { var param = Expression.Parameter(typeof(NewParam)); return Expression.Lambda>( expression.Body.Replace(expression.Parameters[0], param) , param); }
这使用Replace
方法将一个表达式的所有实例替换为另一个表达式。 定义是:
internal class ReplaceVisitor : ExpressionVisitor { private readonly Expression from, to; public ReplaceVisitor(Expression from, Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression node) { return node == from ? to : base.Visit(node); } } public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx) { return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); }
现在我们可以使用这个方法(应该给出一个更好的名字),如下所示:
Expression> oldExpression = whatever; Expression> newExpression = Foo(oldExpression);
当然,由于Func
实际上是关于其参数的协变,我们可以确定对此方法的任何调用都会生成不会添加运行时故障点的表达式。
如果你愿意的话,你可以通过16种不同类型的Func
轻松地为Func
制作这个版本,只需为每个类型创建一个参数表达式,并替换所有旧的参数表达式。新的。 这很乏味,但只是遵循这种模式。 鉴于旧的和新的参数类型都需要一个generics参数,并且没有办法推断出参数,那就太麻烦了。
幸运的是,对于你想要的东西,没有必要使用表达式树。 您需要的是增强模板限制:
public static IQueryable FilterByDate (this IQueryable src, DateTime startDate, DateTime endDate) where TModel: class, IRequiredDate { return src.Where(x => x.Date >= startDate && x.Date <= endDate); }
一点解释。 使用LINQPad,您可以看到在删除class
要求时生成的表达式树是不同的。 当限制存在时, Where
子句是这样的:
.Where (x => (x => x.Date >= startDate && x.Date <= endDate))
而当删除限制时,表达式更改如下:
.Where (x => (x => (((IRequiredDate)x).Date >= startDate) && (((IRequiredDate)x).Date <= endDate)))
表达式树有一些额外的强制转换,这就是为什么在这种forms下,entity framework告诉你它无法使用IRequiredDate
类型的IRequiredDate
。
我只有几分钟,所以我没有想到这一点。 这有帮助吗?
Expression> exp1 = (list => list.Count > 0); Expression> exp2 = (list => exp1.Compile()(list)); Expression, bool>> exp3 = (list => exp1.Compile()(list));
我有点展示你想要的东西。