3

我正在使用标准访问者模式来遍历 LINQ 表达式树,以生成动态 SQL WHERE 子句。

我的问题是,与 C# 不同,您不能在 SQL 中使用独立的布尔表达式;您必须将其与 1 或 0 进行比较。

给定这个假设的 lambda 表达式:

h => h.Enabled || h.Enabled == false

很容易错误地生成此代码:

WHERE Enabled OR Enabled = 0

或此代码:

WHERE (Enabled = 1) OR (Enabled = 1) = 0

两者当然都会产生 SQL 错误。当我深入研究子树以找出可能的情况时,我应该应用什么逻辑来解决这个问题,而我的代码开始看起来真的很迟钝?

编辑:上面的例子当然是多余的——我只是用它来说明一点。

可以创建此场景的示例:

h => h.Enabled
h => h.Enabled == enabled
h => h.Enabled == true

当然,最后一个例子的风格很差,但我的代码被设计为独立于程序员的技能水平工作,所以不满足冗余场景对我来说是一种糟糕的形式。

4

3 回答 3

5

以下情况非常简单:

h => h.Enabled == enabled
h => h.Enabled == true

这些是BinaryExpression节点,您可以直接将它们翻译成:

WHERE (Enabled = @p0)
WHERE (Enabled = 1)

您需要处理的特殊情况是:

h => h.Enabled
h => !h.Enabled

这些在表达式树中以不同的方式表示(作为 a MemberExpression)。因此,您需要对 the 进行特殊处理MemberExpression并确定它是否正在访问布尔属性。如果是,则将其转换为规范形式(UnaryExpression在第二个示例中检测 ):

WHERE (Enabled = 1)
WHERE (Enabled = 0)

或者,您可能能够预处理表达式树并将任何特殊情况转换为其规范(表达式树)形式。例如,任何MemberExpression符合条件的节点都可以转换为正确的BinaryExpression.

于 2009-11-23T09:12:04.063 回答
0

在评估运算符之前不可能完全处理操作数吗?

IE。评估每个:

h => h.Enabled
h => h.Enabled == enabled
h => h.Enabled == true

WHERE (Enabled = 1)

然后在 lambda 中包含运算符的情况下,使用等效的 SQL 处理呈现的操作数的集合,以满足运算符的要求。

于 2009-11-23T09:08:44.857 回答
0

其他人可能有同样的问题。我通过将 last 保存BinaryExpression到变量中解决了这个问题 这将解决以下不同的布尔子句

其中(f => f.IsActive == true || !f.IsActive == false || (f.Name == "adfd") && (f.IsActive || !f.IsBin) )

这是处理独立布尔值的代码

private string GetFilterMember(MemberExpression exp)
{
    var str = exp.ToString();
    if (str.Count(f => f == '.') > 1)
    {
        var expType = exp.Type;
        var declareType = exp.Member.DeclaringType;
        if (declareType == typeof(string))
        {
            if (exp.Member.Name == StringMethods.Length)
            {
                return $"{StringMethods.GetODataMethod(StringMethods.Length)}({exp.NameLeftPart()})";
            }
        }
        else if (declareType == typeof(DateTime))
        {

        }
    }
    if (exp.Type == typeof(bool))
    {
        if (prviousBinaryExp != null && (prviousBinaryExp.Left == exp || prviousBinaryExp.Right == exp || (prviousBinaryExp.Left.NodeType == ExpressionType.Not && ((UnaryExpression)prviousBinaryExp.Left).Operand == exp) || (prviousBinaryExp.Right.NodeType == ExpressionType.Not && ((UnaryExpression)prviousBinaryExp.Right).Operand == exp)) && (prviousBinaryExp.NodeType != ExpressionType.Equal && prviousBinaryExp.NodeType != ExpressionType.NotEqual))
        {          
            return $"{exp.Member.Name}{ODataQueryOperator.Equal}{FilterBuilder(Expression.Constant(true))}";
        }
    }
    return exp.Member.Name;
}
BinaryExpression prviousBinaryExp = null;
protected virtual string FilterBuilder(Expression exp)
{

    switch (exp.NodeType)
    {
        case ExpressionType.OrElse:
        case ExpressionType.AndAlso:
        case ExpressionType.Equal:
            prviousBinaryExp = (BinaryExpression)exp;
            return BinaryExpressionBuilder(((BinaryExpression)exp).Left, ODataQueryOperator.GetLogicalOperator(exp.NodeType), ((BinaryExpression)exp).Right);
        case ExpressionType.Constant:
            return GetConstant(exp.Type, ((ConstantExpression)exp).Value);
        case ExpressionType.MemberAccess:
            return GetFilterMember((MemberExpression)exp);
        case ExpressionType.Not:
            return UnaryExpressionBuilder((UnaryExpression)exp);
        case ExpressionType.Call:
            return MethodBuilder((MethodCallExpression)exp);
        default:
            return string.Empty;

    }
}

不向下这个变量BinaryExpression prviousBinaryExp = null; 当你点击BinaryExpression然后将该表达式保存在上面的变量中

case ExpressionType.OrElse:
case ExpressionType.AndAlso:
case ExpressionType.Equal:
     prviousBinaryExp = (BinaryExpression)exp;
     return BinaryExpressionBuilder(((BinaryExpression)exp).Left, ODataQueryOperator.GetLogicalOperator(exp.NodeType), ((BinaryExpression)exp).Right);

这是检查先前表达式是否为 binaryexpression 并且当前表达式是否为BinaryExpression's LeftorRight表达式的一部分的逻辑。我们将检查Nodtype哪个不应该是ExpressionType.EqualExpressionType.NotEqual(f.IsActive == true)。我们还将确保 ifLeftRightof BinaryExpressionis UnaryExpressionthan 我们将检查它UnaryExpression.Operand是否等于当前表达式。这将处理 !f.Enabled 或 !f.IsActive 等。

最后,我将 Expression 转换为 OData 等效字符串。哪个是

        // converted String IsActive to OData equivalent
        //         IsActive          eq            true
        return $"{exp.Member.Name}{ODataQueryOperator.Equal}{FilterBuilder(Expression.Constant(true))}";
于 2021-09-16T07:09:05.357 回答