我正在使用Entity Framework与数据库进行交谈,但是我试图将这些细节隐藏在更高级别的模块中,我不希望实体对象本身被暴露出来.
但是,我希望使我的界面在用于查找工作信息的标准中非常灵活.例如,用户界面应该允许用户执行复杂的查询,例如“给我所有在”上午10点到11点之间运行的命名为“hello”的作业失败.显然,这看起来像一个动态构建的表达式树的工作.
所以我希望我的数据层(存储库)能够做的是接受类型为Expression< Func< string,DateTime,ResultCode,long,bool>>的LINQ表达式. (lambda表达式),然后在后台将该lambda转换为我的Entity Framework ObjectContext可以用作Where()子句中的过滤器的表达式.
简而言之,我试图转换Expression< Func< string,bool>>类型的lambda表达式.到表达< Func< svc_JobAudit,bool>>>>>其中,svc_JobAudit是对应于存储作业信息的表的Entity Framework数据对象. (第一代表中的四个参数对应于作业的名称,运行时的结果,以及分别在MS中花费了多长时间)
我正在使用ExpressionVisitor类非常好的进度,直到我打砖墙,并收到一个InvalidOperationException与此错误消息:
When called from ‘VisitLambda’,rewriting a node of type
‘System.Linq.Expressions.ParameterExpression’ must return a non-null
value of the same type. Alternatively,override ‘VisitLambda’ and
change it to not visit children of this type.
我完全感到困惑为什么它不会允许我将参考参数的表达式节点转换为引用属性的节点?还有另外一个办法吗?
以下是一些示例代码:
namespace ExpressionTest { class Program { static void Main(string[] args) { Expression<Func<string,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,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; } } }
解决方法
>我误会了如何访问Lambda表达式.我仍然返回一个与旧代表匹配的lambda,而不是返回一个新的lambda来匹配新的委托.
>我需要持有对新的ParameterExpression实例的引用,我没有做.
新代码看起来像这样(注意访问者现在如何接受对与Entity Framework数据对象匹配的ParameterExpression的引用):
class Program { const string conString = @"myDB"; static void Main(string[] args) { Expression<Func<string,byte,bool>> expression = (jobName,ranAt,resultCode,elapsed) => jobName == "Email Notifications" && resultCode == (byte)ResultCode.Failed; var criteria = ConvertExpression(expression); using (MyDataContext dataContext = new MyDataContext(conString)) { List<svc_JobAudit> jobs = dataContext.svc_JobAudit.Where(criteria).ToList(); } } private static Expression<Func<svc_JobAudit,bool>> expression) { var jobAuditParameter = Expression.Parameter(typeof(svc_JobAudit),"jobAudit"); var newExpression = Expression.Lambda<Func<svc_JobAudit,bool>>(new ReplaceVisitor().Modify(expression.Body,jobAuditParameter),jobAuditParameter); return newExpression; } } class ReplaceVisitor : ExpressionVisitor { private ParameterExpression parameter; public Expression Modify(Expression expression,ParameterExpression parameter) { this.parameter = parameter; return Visit(expression); } protected override Expression VisitLambda<T>(Expression<T> node) { return Expression.Lambda<Func<svc_JobAudit,bool>>(Visit(node.Body),Expression.Parameter(typeof(svc_JobAudit))); } protected override Expression VisitParameter(ParameterExpression node) { if (node.Type == typeof(string)) { return Expression.Property(parameter,"JobName"); } else if (node.Type == typeof(DateTime)) { return Expression.Property(parameter,"RanAt"); } else if (node.Type == typeof(byte)) { return Expression.Property(parameter,"Result"); } else if (node.Type == typeof(long)) { return Expression.Property(parameter,"Elapsed"); } throw new InvalidOperationException(); } }