我正在编写一个需要来自源的数据的工具。这个源将是用户指定的,可能是SQL后端,专有数据库,平面文件系统,谁知道。
我喜欢我的界面来获取这些类型的查询,使用Linq,因为它似乎是最友好的C#,并且可以利用很多.NET代码。
我一直在做一些研究,并且已经开始通过伟大的教程here建立一个IQueryProvider。它让我占了很大的一部分,但现在我对让用户将表达式树转换为自定义代码的最佳方式感到困惑。
我试图找出为用户提供简单界面的最佳方式,以指定如何将表达式树转换为自定义"代码" (即" SQL"),它看起来相当繁琐复杂 - 我想是因为它就是这样。
我的问题是,将表达式树转换为自定义语言的最佳方法是什么?
我最近能说的是我要使用' Context'我要使用自定义解析逻辑的类,但我使用的API看起来相当低级 - 我是否可以执行更高级别的操作来简单地将操作映射到字符串?
答案 0 :(得分:4)
将表达式树转换为自定义查询语言并不容易或直接。您可能想尝试一下LinqExtender
http://mehfuzh.github.com/LinqExtender/
它实现了一个访问者模式,用于在linq和你的dsl之间进行转换。
LinqExtender是一个用于构建自定义LINQ提供程序的工具包。它 在原始的IQyeryable上提供一个抽象层 IQueryProvider实现并提供简化的语法树。 此外,它涵盖了诸如投影,方法调用,有序等内容 ,内部成员解析等。因此开发人员可以更专注 他的主要任务减去复杂性
答案 1 :(得分:1)
通常,将Tree结构转换为其他形式的最佳方法是使用visitor pattern。
专门查看msdn。上的ExpressionVisitor课程。
答案 2 :(得分:0)
所以,我确实调查了访客模式,但我无法按照我喜欢的方式工作,所以我有点破解了一个解决方案。 :/
我已经使用基本示例创建了一个基础QueryContext,用于解析树并构建字符串集合。我最终得到的是这样的。这绝不是完整的,但这是一个不错的开始:
public object Execute(Expression expression, bool IsEnumerable)
{
// Find the call to Where() and get the lambda expression predicate.
InnermostWhereFinder whereFinder = new InnermostWhereFinder();
MethodCallExpression whereExpression = whereFinder.GetInnermostWhere(expression);
LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)(whereExpression.Arguments[1])).Operand;
// Send the lambda expression through the partial evaluator.
lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression);
// Assemble the strings necessary to build this.
var strings = new List<string>();
GetStrings(lambdaExpression.Body, strings);
var query = String.Join(" ", strings);
return ExecuteQuery(query);
}
public abstract object ExecuteQuery(string whereClause);
public abstract Dictionary<ExpressionType, string> ExpressionTypeToStringMap { get; }
public abstract string FormatFieldName(string fieldName);
public abstract string FormatConstant(string constant);
void GetStrings(System.Linq.Expressions.Expression expression, List<string> toReturn)
{
if (expression is BinaryExpression)
{
// Binary expression. Recurse and add to the list.
GetStrings((BinaryExpression)(expression), toReturn);
}
else if (expression is MemberExpression)
{
var e = (MemberExpression)(expression);
toReturn.Add(FormatFieldName(e.Member.Name));
}
else if (expression is ConstantExpression)
{
var e = (ConstantExpression)(expression);
toReturn.Add(FormatConstant((string)(e.Value)));
}
else
{
throw new NotImplementedException("Unaware of how to handle type " + expression.GetType().ToString());
}
}
string NodeTypeToString(ExpressionType type)
{
var map = ExpressionTypeToStringMap;
if(map.ContainsKey(type))
{
return map[type];
}
throw new NotImplementedException("Type '" + type.ToString() + "' not implemented in ExpressionTypeToStringMap.");
}
void GetStrings(BinaryExpression expression, List<string> toReturn)
{
toReturn.Add("(");
if (expression.Left != null)
GetStrings(expression.Left, toReturn);
toReturn.Add(NodeTypeToString(expression.NodeType));
if (expression.Right != null)
GetStrings(expression.Right, toReturn);
toReturn.Add(")");
}
欢迎更好的实施,但至少现在我已经畅通无阻。