Beispiel #1
0
        /// <summary>
        /// バインド値がDapperAid固有のSQL条件式記述用メソッドで値指定している場合は、
        /// SQL条件式生成メソッドを呼び出してパラメータバインド・SQL組み立てを行います。
        /// </summary>
        /// <param name="parameters">パラメーターオブジェクト</param>
        /// <param name="column">バインド対象のカラム</param>
        /// <param name="expression">バインド値をあらわす式木</param>
        /// <param name="opIsNot">条件式をnotで生成する場合はtrue</param>
        /// <returns>生成されたSQL表現文字列。ただしバインド値がDapperAid固有のSQL条件式記述用メソッドではなかった場合null</returns>
        protected string TryBindSqlExpr(DynamicParameters parameters, TableInfo.Column column, Expression expression, bool opIsNot)
        {
            var methodCallExpression = ExpressionHelper.CastTo <MethodCallExpression>(expression);

            if (methodCallExpression != null && methodCallExpression.Method != null && typeof(ISqlExpr).IsAssignableFrom(methodCallExpression.Method.DeclaringType))
            {
                return(InstanceCreator.Create <ISqlExpr>(methodCallExpression.Method.DeclaringType).BuildSql(
                           methodCallExpression.Method.Name,
                           methodCallExpression.Arguments,
                           this,
                           parameters,
                           column,
                           opIsNot));
            }
            return(null);
        }
Beispiel #2
0
        /// <summary>
        /// 引数で指定された条件式ラムダに基づきWhere条件を生成します。
        /// </summary>
        /// <param name="parameters">Dapperパラメーターオブジェクト</param>
        /// <param name="tableInfo">テーブル情報</param>
        /// <param name="expression">式木(ラムダ)により記述された条件式</param>
        /// <returns>条件式のSQL</returns>
        private string BuildWhere(DynamicParameters parameters, TableInfo tableInfo, Expression expression)
        {
            // ローカル関数相当:ExpressionがWhere条件となるカラムなら、そのカラム情報を返す
            Func <Expression, TableInfo.Column> getIfOperandIsConditionColumn = (exp) =>
            {
                var me = ExpressionHelper.CastTo <MemberExpression>(exp);
                if (me == null || me.Expression == null || me.Expression.NodeType != ExpressionType.Parameter)
                {   // ラムダ式の引数についてのメンバーでなければ、Where条件となるカラムとはみなさない
                    return(null);
                }
                var prop = me.Member as PropertyInfo;
                if (prop == null)
                {   // プロパティ項目でなければ、Where条件となるカラムとはみなさない
                    return(null);
                }
                // tableInfoのカラムに対応するなら、そのカラム情報を返す
                return(tableInfo.Columns.Where(c => c.PropertyInfo == prop).FirstOrDefault());
            };

            var binaryExpr = ExpressionHelper.CastTo <BinaryExpression>(expression);

            if (binaryExpr == null)
            {   // 二項演算子以外の特殊な表現が指定されている場合の処理
                // 「!」NOT指定かどうか/bool型のプロパティが指定されているかを判定
                var unaryExpr = ExpressionHelper.CastTo <UnaryExpression>(expression);
                var isNot     = (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Not);
                var column    = getIfOperandIsConditionColumn(isNot ? unaryExpr.Operand : expression);
                if (column != null && column.PropertyInfo.PropertyType == typeof(bool))
                {   // boolカラム値の条件として組み立てる
                    return(column.Name + "=" + (isNot ? FalseLiteral : TrueLiteral));
                }
                if (isNot)
                {   // NOT条件として組み立てる
                    return("not(" + BuildWhere(parameters, tableInfo, unaryExpr.Operand) + ")");
                }

                // DapperAid固有のSQL条件式記述用メソッドで式木を記述している場合は、SQL条件式生成メソッドを呼び出してSQLを組み立てる
                var sqlExprBindResult = TryBindSqlExpr(parameters, null, expression, isNot);
                if (sqlExprBindResult != null)
                {
                    return(sqlExprBindResult);
                }

                // bool値となる変数や処理を記述していればその値を組み立てる
                var boolValue = ExpressionHelper.EvaluateValue(expression);
                if (boolValue is bool)
                {
                    return(((bool)boolValue == true) ? TrueLiteral : FalseLiteral);
                }

                // いずれでもなければ例外
                throw new InvalidExpressionException(expression.ToString());
            }

            // And/Or条件の解釈
            switch (binaryExpr.NodeType)
            {
            case ExpressionType.AndAlso:
                var andLeft = BuildWhere(parameters, tableInfo, binaryExpr.Left);
                if (andLeft == FalseLiteral)
                {       // 「left && right」のうちleftでfalseが確定している場合は、右辺の展開を行わない
                    return(andLeft);
                }
                var andRight = BuildWhere(parameters, tableInfo, binaryExpr.Right);
                // 左辺右辺いずれかでtrueが確定していればもう一方のみを条件とし、そうでなければand条件式を組み立て
                return((andLeft == TrueLiteral) ? andRight
                        : (andRight == TrueLiteral) ? andLeft
                                                    : andLeft + " and " + andRight);

            case ExpressionType.OrElse:
                var orLeft = BuildWhere(parameters, tableInfo, binaryExpr.Left);
                if (orLeft == TrueLiteral)
                {       // 「left || right」のうちleftでtrueが確定している場合は、右辺の展開を行わない
                    return(orLeft);
                }
                var orRight = BuildWhere(parameters, tableInfo, binaryExpr.Right);
                // 左辺右辺いずれかでfalseが確定していればもう一方のみを条件とし、そうでなければor条件式を組み立て
                return((orLeft == FalseLiteral) ? orRight
                        : (orRight == FalseLiteral) ? orLeft
                                                    : "(" + orLeft + " or " + orRight + ")");
            }

            var leftMember  = getIfOperandIsConditionColumn(binaryExpr.Left);
            var rightMember = getIfOperandIsConditionColumn(binaryExpr.Right);

            if (leftMember == null && rightMember == null)
            {   // 両辺ともカラム指定ではない:具体的なbool値に展開
                var boolValue = ExpressionHelper.EvaluateValue(binaryExpr);
                if (!(boolValue is bool))
                {
                    throw new InvalidExpressionException(binaryExpr.ToString());
                }
                return((bool)boolValue ? TrueLiteral : FalseLiteral);
            }

            // 比較演算子を解釈
            string op;
            var    opEnd   = string.Empty;
            var    opIsNot = false;

            switch (binaryExpr.NodeType)
            {
            case ExpressionType.Equal:
                op = "="; break;

            case ExpressionType.NotEqual:
                op = "<>"; opIsNot = true; break;

            case ExpressionType.GreaterThan:
                op = ">"; break;

            case ExpressionType.GreaterThanOrEqual:
                op = ">="; break;

            case ExpressionType.LessThan:
                op = "<"; break;

            case ExpressionType.LessThanOrEqual:
                op = "<="; break;

            default:
                throw new InvalidExpressionException(expression.ToString());
            }

            if (leftMember != null && rightMember != null)
            {   // 両辺ともカラム名指定の場合はパラメータ値取得不要、両辺のカラム名に基づき条件式を組み立て
                return(leftMember.Name + op + rightMember.Name);
            }

            var condColumn      = leftMember ?? rightMember;
            var valueExpression = ExpressionHelper.CastTo <Expression>(condColumn == leftMember ? binaryExpr.Right : binaryExpr.Left);

            // DapperAid固有のSQL条件式記述用メソッドで値指定している場合は、SQL条件式生成メソッドを呼び出してSQLを組み立てる
            {
                var sqlExprBindResult = TryBindSqlExpr(parameters, condColumn, valueExpression, opIsNot);
                if (sqlExprBindResult != null)
                {
                    return(sqlExprBindResult);
                }
            }

            // カラム指定でない側の指定値を把握しSQL条件式を組み立てる
            var value = ExpressionHelper.EvaluateValue(valueExpression);

            if (value == null)
            {
                return(condColumn.Name + " is" + (opIsNot ? " not" : "") + " null");
            }
            else
            {
                var bindedValueSql = AddParameter(parameters, condColumn.PropertyInfo.Name, value);
                return(condColumn == leftMember
                    ? condColumn.Name + op + bindedValueSql
                    : bindedValueSql + op + condColumn.Name);
            }
        }
Beispiel #3
0
        /// <summary>
        /// 引数で指定された条件式ラムダに基づきWhere条件を生成します。
        /// </summary>
        /// <param name="parameters">Dapperパラメーターオブジェクト</param>
        /// <param name="tableInfo">テーブル情報</param>
        /// <param name="expression">式木(ラムダ)により記述された条件式</param>
        /// <returns>条件式のSQL</returns>
        private string BuildWhere(DynamicParameters parameters, TableInfo tableInfo, Expression expression)
        {
            // ローカル関数相当:ExpressionがWhere条件となるカラムなら、そのカラム情報を返す
            Func <Expression, TableInfo.Column> getIfOperandIsConditionColumn = (exp) =>
            {
                var me = ExpressionHelper.CastTo <MemberExpression>(exp);
                if (me == null || me.Expression == null || me.Expression.NodeType != ExpressionType.Parameter)
                {   // ラムダ式の引数についてのメンバーでなければ、Where条件となるカラムとはみなさない
                    return(null);
                }
                var prop = me.Member as PropertyInfo;
                if (prop == null)
                {   // プロパティ項目でなければ、Where条件となるカラムとはみなさない
                    return(null);
                }
                // tableInfoのカラムに対応するなら、そのカラム情報を返す
                return(tableInfo.Columns.Where(c => c.PropertyInfo == prop).FirstOrDefault());
            };

            var binaryExpr = ExpressionHelper.CastTo <BinaryExpression>(expression);

            if (binaryExpr == null)
            {   // 二項演算子以外の特殊な表現が指定されている場合の処理
                // 「!」NOT指定かどうか/bool型のプロパティが指定されているかを判定
                var unaryExpr = ExpressionHelper.CastTo <UnaryExpression>(expression);
                var isNot     = (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Not);
                var column    = getIfOperandIsConditionColumn(isNot ? unaryExpr.Operand : expression);
                if (column != null && column.PropertyInfo.PropertyType == typeof(bool))
                {   // boolカラム値の条件として組み立てる
                    return(column.Name + "=" + (isNot ? "false" : "true"));
                }
                if (isNot)
                {   // NOT条件として組み立てる
                    return("not(" + BuildWhere(parameters, tableInfo, unaryExpr.Operand) + ")");
                }

                // 特定のメソッド呼び出しが指定されているかを判定
                var methodCallExpr = ExpressionHelper.CastTo <MethodCallExpression>(expression);
                if (methodCallExpr != null && methodCallExpr.Method != null && methodCallExpr.Method.DeclaringType == typeof(ToSql))
                {
                    // ※C#6.0以降でないとnameofが利用できないため定数定義
                    const string NameofEval = "Eval"; //nameof(ToSql.Eval);

                    if (methodCallExpr.Method.Name == NameofEval)
                    {   // ToSql.Eval(string):指定されたSQL文字列を直接埋め込む
                        var argumentExpression = methodCallExpr.Arguments[0];
                        if (argumentExpression.Type == typeof(string))
                        {
                            return("(" + ExpressionHelper.EvaluateValue(argumentExpression) + ")");
                        }
                    }
                }

                // いずれでもなければ例外
                throw new InvalidExpressionException(expression.ToString());
            }

            // And/Or条件の解釈
            switch (binaryExpr.NodeType)
            {
            case ExpressionType.AndAlso:
                var andLeft = BuildWhere(parameters, tableInfo, binaryExpr.Left);
                if (andLeft == "false")
                {       // 「left && right」のうちleftでfalseが確定している場合は、右辺の展開を行わない
                    return(andLeft);
                }
                var andRight = BuildWhere(parameters, tableInfo, binaryExpr.Right);
                if (andLeft == "true")
                {       // 「left && right」のうちleftがtrueなら、右辺のみを返す
                    return(andRight);
                }
                return(andLeft + " and " + andRight);

            case ExpressionType.OrElse:
                var orLeft = BuildWhere(parameters, tableInfo, binaryExpr.Left);
                if (orLeft == "true")
                {       // 「left || right」のうちleftでtrueが確定している場合は、右辺の展開を行わない
                    return(orLeft);
                }
                var orRight = BuildWhere(parameters, tableInfo, binaryExpr.Right);
                if (orLeft == "false")
                {       // 「left || right」のうちleftがfalseなら、右辺のみを返す
                    return(orRight);
                }
                return("(" + orLeft + ") or (" + orRight + ")");
            }

            var leftMember  = getIfOperandIsConditionColumn(binaryExpr.Left);
            var rightMember = getIfOperandIsConditionColumn(binaryExpr.Right);

            if (leftMember == null && rightMember == null)
            {   // 両辺ともカラム指定ではない:具体的なbool値に展開
                var boolValue = ExpressionHelper.EvaluateValue(binaryExpr);
                if (!(boolValue is bool))
                {
                    throw new InvalidExpressionException(binaryExpr.ToString());
                }
                return((bool)boolValue ? "true" : "false");
            }

            // 比較演算子を解釈
            string op;
            var    opEnd   = string.Empty;
            var    opIsNot = false;

            switch (binaryExpr.NodeType)
            {
            case ExpressionType.Equal:
                op = "="; break;

            case ExpressionType.NotEqual:
                op = "<>"; opIsNot = true; break;

            case ExpressionType.GreaterThan:
                op = ">"; break;

            case ExpressionType.GreaterThanOrEqual:
                op = ">="; break;

            case ExpressionType.LessThan:
                op = "<"; break;

            case ExpressionType.LessThanOrEqual:
                op = "<="; break;

            default:
                throw new InvalidExpressionException(expression.ToString());
            }

            if (leftMember != null && rightMember != null)
            {   // 両辺ともカラム名指定の場合はパラメータ値取得不要、両辺のカラム名に基づき条件式を組み立て
                return(leftMember.Name + op + rightMember.Name);
            }

            var condColumn      = leftMember ?? rightMember;
            var valueExpression = ExpressionHelper.CastTo <Expression>(condColumn == leftMember ? binaryExpr.Right : binaryExpr.Left);

            if (valueExpression is MethodCallExpression)
            {   // 値指定時に特定のToSqlメソッドを通している場合は、メソッドに応じたSQLを組み立てる
                var method = (valueExpression as MethodCallExpression).Method;
                if (method.DeclaringType == typeof(ToSql))
                {
                    // ※C#6.0以降でないとnameofが利用できないため定数定義とする
                    const string NameofLike    = "Like";    //nameof(ToSql.Like);
                    const string NameofIn      = "In";      //nameof(ToSql.In);
                    const string NameofBetween = "Between"; //nameof(ToSql.Between);

                    if (method.Name == NameofLike)
                    {   // ToSql.Like(string):比較演算子をLike演算子とする
                        op = (opIsNot ? " not" : "") + " like ";
                        valueExpression = (valueExpression as MethodCallExpression).Arguments[0];
                    }
                    else if (method.Name == NameofIn)
                    {
                        valueExpression = (valueExpression as MethodCallExpression).Arguments[0];
                        if (valueExpression.Type == typeof(string))
                        {   // ToSql.In(string): INサブクエリとして指定された文字列を直接埋め込む
                            return(condColumn.Name + (opIsNot ? " not" : "") + " in(" + ExpressionHelper.EvaluateValue(valueExpression) + ")");
                        }
                        else
                        {   // ToSql.In(コレクション): In演算子を組み立てる
                            return(BuildWhereIn(parameters, condColumn, opIsNot, ExpressionHelper.EvaluateValue(valueExpression)));
                        }
                    }
                    else if (method.Name == NameofBetween)
                    {    // ToSql.Between(値1, 値2): Between演算子を組み立て、パラメータを2つバインドする。nullの可能性は考慮しない
                        var value1 = ExpressionHelper.EvaluateValue((valueExpression as MethodCallExpression).Arguments[0]);
                        var value2 = ExpressionHelper.EvaluateValue((valueExpression as MethodCallExpression).Arguments[1]);
                        return(condColumn.Name + (opIsNot ? " not" : "") + " between "
                               + AddParameter(parameters, condColumn.PropertyInfo.Name, value1)
                               + " and "
                               + AddParameter(parameters, condColumn.PropertyInfo.Name, value2));
                    }
                    else
                    {
                        throw new InvalidExpressionException(expression.ToString());
                    }
                }
            }
            var value = ExpressionHelper.EvaluateValue(valueExpression);

            if (value == null)
            {
                return(condColumn.Name + " is" + (opIsNot ? " not" : "") + " null");
            }
            else
            {
                var bindedValueSql = AddParameter(parameters, condColumn.PropertyInfo.Name, value);
                return(condColumn == leftMember
                    ? condColumn.Name + op + bindedValueSql
                    : bindedValueSql + op + condColumn.Name);
            }
        }