Вот реализация, которая, по крайней мере, преобразует ваш ввод в допустимое выражение SQL. Вам нужно реализовать больше типов выражений самостоятельно, но это дает вам представление о том, как это работает.
Этот ответ очень похож на ответ Казецукай, но он использует Expression.NodeType
для поиска операторов, поскольку в дереве выражений не будет MethodInfos
.
Также имейте в виду, что это создает больше круглых скобок, чем на самом деле необходимо. Чтобы уменьшить количество круглых скобок, выражение необходимо проанализировать дополнительно с учетом приоритета операторов в SQL.
public static string GetSqlExpression(Expression expression)
{
if (expression is BinaryExpression)
{
return string.Format("({0} {1} {2})",
GetSqlExpression(((BinaryExpression)expression).Left),
GetBinaryOperator((BinaryExpression)expression),
GetSqlExpression(((BinaryExpression)expression).Right));
}
if (expression is MemberExpression)
{
MemberExpression member = (MemberExpression)expression;
// it is somewhat naive to make a bool member into "Member = TRUE"
// since the expression "Member == true" will turn into "(Member = TRUE) = TRUE"
if (member.Type == typeof(bool))
{
return string.Format("([{0}] = TRUE)", member.Member.Name);
}
return string.Format("[{0}]", member.Member.Name);
}
if (expression is ConstantExpression)
{
ConstantExpression constant = (ConstantExpression)expression;
// create a proper SQL representation for each type
if (constant.Type == typeof(int) ||
constant.Type == typeof(string))
{
return constant.Value.ToString();
}
if (constant.Type == typeof(bool))
{
return (bool)constant.Value ? "TRUE" : "FALSE";
}
throw new ArgumentException();
}
throw new ArgumentException();
}
public static string GetBinaryOperator(BinaryExpression expression)
{
switch (expression.NodeType)
{
case ExpressionType.Equal:
return "=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.OrElse:
return "OR";
case ExpressionType.AndAlso:
return "AND";
case ExpressionType.LessThan:
return "<";
case ExpressionType.GreaterThan:
return ">";
default:
throw new ArgumentException();
}
}
Результат:
(((([Scale] < 5) OR ([Scale] > 20)) AND ([Scale] <> -100)) OR ([IsExempt] = TRUE))
Вызовите метод следующим образом:
string sqlExpression = GetSqlExpression(exprTree.Body);
Я бы предложил построить дерево выражений более функциональным способом. Вместо того, чтобы строить Func<bool>
из бетона foo
, вы должны использовать Func<Foo, bool>
. Тем не менее, это будет работать в любом случае. Это просто не выглядит правильно.
Expression<Func<Foo, bool>> exprTree =
(foo) => ((foo.Scale < 5 || foo.Scale > 20) && foo.Scale != -100) || foo.IsExempt == true;
Очевидно, что обычно нет необходимости создавать текст SQL самостоятельно, если вы можете использовать LINQ to Entities. И LINQ для сущностей, и деревьев выражений требуют .NET 3.5, и вы действительно можете Перевести оператор LINQ в sql а>.
Я не уверен, что такое выражение, как IsExempt = TRUE
, будет работать на SQL Server. Я думаю, что это должно быть IsExempt = 1
, так как тип данных bit
. Также такие выражения, как Value == null
или Value != null
, необходимо обрабатывать отдельно, поскольку вы не можете использовать Value = NULL
или Value <> NULL
в выражении SQL. Должно быть Value IS NULL
или Value IS NOT NULL
.
person
pescolino
schedule
02.03.2013
add(5, add(3, 2)
) илиadd(add(5, 3), 2)
). - person Alxandr   schedule 02.03.2013