我正在为我们系统的一部分编写数据层,它记录每天运行的自动化作业的信息——作业的名称、运行的时间、结果等。
我正在使用实体框架与数据库交谈,但我试图将这些细节隐藏在更高级别的模块中,并且我不希望实体对象本身被暴露。
但是,我想让我的界面在用于查找工作信息的标准方面非常灵活。例如,用户界面应该允许用户执行复杂的查询,例如“给我所有名为 'hello' 的作业,在上午 10:00 到上午 11:00 之间运行但失败了”。显然,这看起来像是动态构建Expression
树的工作。
所以我希望我的数据层(存储库)能够做的是接受类型的 LINQ 表达式Expression<Func<string, DateTime, ResultCode, long, bool>>
(lambda 表达式),然后在幕后将该 lambda 转换为我的实体框架ObjectContext
可以用作Where()
子句中的过滤器的表达式.
简而言之,我正在尝试将类型的 lambda 表达式转换Expression<Func<string, DateTime, ResultCode, long, bool>>
为Expression<Func<svc_JobAudit, bool>>
,其中svc_JobAudit
Entity Framework 数据对象对应于存储作业信息的表。(第一个delegate中的四个参数分别对应job的名字,什么时候运行,结果,在MS里用了多长时间)
我在课堂上取得了很好的进步,ExpressionVisitor
直到我碰壁并收到InvalidOperationException
以下错误消息:
从“VisitLambda”调用时,重写“System.Linq.Expressions.ParameterExpression”类型的节点必须返回相同类型的非空值。或者,覆盖“VisitLambda”并将其更改为不访问此类型的子项。
我完全感到困惑。为什么它不允许我将引用参数的表达式节点转换为引用属性的节点?还有其他方法可以解决这个问题吗?
这是一些示例代码:
namespace ExpressionTest
{
class Program
{
static void Main(string[] args)
{
Expression<Func<string, DateTime, ResultCode, long, bool>> expression = (myString, myDateTime, myResultCode, myTimeSpan) => myResultCode == ResultCode.Failed && myString == "hello";
var result = ConvertExpression(expression);
}
private static Expression<Func<svc_JobAudit, bool>> ConvertExpression(Expression<Func<string, DateTime, ResultCode, long, bool>> expression)
{
var newExpression = Expression.Lambda<Func<svc_JobAudit, bool>>(new ReplaceVisitor().Modify(expression), Expression.Parameter(typeof(svc_JobAudit)));
return newExpression;
}
}
class ReplaceVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(string))
{
return Expression.Property(Expression.Parameter(typeof(svc_JobAudit)), "JobName");
}
return node;
}
}
}