Represents a SQL CASE WHEN expression.
Inheritance: Remotion.Linq.Clauses.Expressions.ExtensionExpression
    public void SetUp ()
    {
      _predicate1 = Expression.Constant (true);
      _predicate2 = Expression.Constant (false, typeof (bool?));
      _value1 = Expression.Constant (17);
      _value2 = Expression.Constant (4);
      _nullableValue1 = Expression.Constant (47, typeof (int?));
      _elseValue = Expression.Constant (42);

      _caseExpressionWithElse = new SqlCaseExpression (
          typeof (int),
          new[] { new SqlCaseExpression.CaseWhenPair (_predicate1, _value1), new SqlCaseExpression.CaseWhenPair (_predicate2, _value2) },
          _elseValue);
      _caseExpressionWithoutElse = new SqlCaseExpression (
          typeof (int?),
          new[] { new SqlCaseExpression.CaseWhenPair (_predicate1, _nullableValue1), new SqlCaseExpression.CaseWhenPair (_predicate2, _value2) },
          null);
    }
    protected override Expression VisitMemberExpression (MemberExpression expression)
    {
      ArgumentUtility.CheckNotNull ("expression", expression);

      var newInnerExpression = VisitExpression (expression.Expression);

      var innerExpressionAsSqlCaseExpression = newInnerExpression as SqlCaseExpression;
      if (innerExpressionAsSqlCaseExpression != null)
      {
        var originalCases = innerExpressionAsSqlCaseExpression.Cases;
        var originalElseCase = innerExpressionAsSqlCaseExpression.ElseCase;
        var newCases = originalCases.Select (c => new SqlCaseExpression.CaseWhenPair (c.When, Expression.MakeMemberAccess (c.Then, expression.Member)));
        var newElseCase = originalElseCase != null ? Expression.MakeMemberAccess (originalElseCase, expression.Member) : null;
        // If there is no else case, ensure that the resulting type is nullable
        var caseExpressionType =
            newElseCase == null && expression.Type.IsValueType && Nullable.GetUnderlyingType (expression.Type) == null
                ? typeof (Nullable<>).MakeGenericType (expression.Type)
                : expression.Type;
        var newSqlCaseExpression = new SqlCaseExpression (caseExpressionType, newCases, newElseCase);
        return VisitExpression (newSqlCaseExpression);
      }

      if (newInnerExpression.NodeType == ExpressionType.Coalesce)
      {
        var innerExpressionAsBinaryExpression = (BinaryExpression) newInnerExpression;
        var newConditionalExpression = Expression.Condition (
            new SqlIsNotNullExpression (innerExpressionAsBinaryExpression.Left), 
            Expression.MakeMemberAccess (innerExpressionAsBinaryExpression.Left, expression.Member), 
            Expression.MakeMemberAccess (innerExpressionAsBinaryExpression.Right, expression.Member));
        return VisitExpression (newConditionalExpression);
      }

      var innerExpressionAsSqlSubStatementExpression = newInnerExpression as SqlSubStatementExpression;
      if (innerExpressionAsSqlSubStatementExpression != null)
      {
        var sqlStatementBuilder = new SqlStatementBuilder (innerExpressionAsSqlSubStatementExpression.SqlStatement);
        var namedExpression = (NamedExpression) sqlStatementBuilder.SelectProjection;
        sqlStatementBuilder.SelectProjection = new NamedExpression (
            namedExpression.Name, VisitExpression (Expression.MakeMemberAccess (namedExpression.Expression, expression.Member)));
        sqlStatementBuilder.RecalculateDataInfo (innerExpressionAsSqlSubStatementExpression.SqlStatement.SelectProjection);
        return new SqlSubStatementExpression (sqlStatementBuilder.GetSqlStatement());
      }

      var memberAsPropertyInfo = expression.Member as PropertyInfo;
      if (memberAsPropertyInfo != null)
      {
        var methodInfo = memberAsPropertyInfo.GetGetMethod();
        if (methodInfo != null)
        {
          var methodCallExpression = Expression.Call (expression.Expression, methodInfo);
          var tranformer = _methodCallTransformerProvider.GetTransformer(methodCallExpression);
          if (tranformer != null)
          {
            var tranformedExpression = tranformer.Transform (methodCallExpression);
            return VisitExpression (tranformedExpression);
          }
        }
      }
      return base.VisitMemberExpression (expression);
    }
    public Expression VisitSqlCaseExpression (SqlCaseExpression expression)
    {
      ArgumentUtility.CheckNotNull ("expression", expression);

      var newCases = VisitList (
          expression.Cases,
          caseWhenPair =>
          {
            var newWhen = ApplyPredicateContext (caseWhenPair.When);
            var newThen = ApplySingleValueContext (caseWhenPair.Then);
            return caseWhenPair.Update (newWhen, newThen);
          });
      var newElseCase = expression.ElseCase != null ? ApplySingleValueContext (expression.ElseCase) : null;
      return expression.Update (newCases, newElseCase);
    }
    public void VisitSqlCaseExpression_WithElse ()
    {
      var case1 = new SqlCaseExpression.CaseWhenPair (new SqlCustomTextExpression ("test1", typeof (bool)), new SqlCustomTextExpression ("value1", typeof (int)));
      var case2 = new SqlCaseExpression.CaseWhenPair (new SqlCustomTextExpression ("test2", typeof (bool)), new SqlCustomTextExpression ("value2", typeof (int)));
      var elseCase = new SqlCustomTextExpression ("elseValue", typeof (int));
      var expression = new SqlCaseExpression (typeof (int), new[] { case1, case2 }, elseCase);

      SqlGeneratingExpressionVisitor.GenerateSql (expression, _commandBuilder, _stageMock);

      Assert.That (_commandBuilder.GetCommandText (), Is.EqualTo ("CASE WHEN test1 THEN value1 WHEN test2 THEN value2 ELSE elseValue END"));
    }
    public void VisitSqlCaseExpression_NoElse ()
    {
      var case1 = new SqlCaseExpression.CaseWhenPair (Expression.Constant (true), Expression.Constant (true));
      var case2 = new SqlCaseExpression.CaseWhenPair (Expression.Constant (false), Expression.Constant (false));
      var expression = new SqlCaseExpression (typeof (bool?), new[] { case1, case2 }, null);

      var result = _valueRequiredVisitor.VisitSqlCaseExpression (expression);

      var expectedCase1 = new SqlCaseExpression.CaseWhenPair (
          Expression.Equal (Expression.Constant (1), new SqlLiteralExpression (1)), new SqlConvertedBooleanExpression (Expression.Constant (1)));
      var expectedCase2 = new SqlCaseExpression.CaseWhenPair (
          Expression.Equal (Expression.Constant (0), new SqlLiteralExpression (1)), new SqlConvertedBooleanExpression (Expression.Constant (0)));
      var expectedExpression = new SqlCaseExpression (typeof (bool?), new[] { expectedCase1, expectedCase2 }, null);
      SqlExpressionTreeComparer.CheckAreEqualTrees (expectedExpression, result);
    }
    public void VisitSqlCaseExpression_RequiresSingleValue_InElse ()
    {
      var entity = SqlStatementModelObjectMother.CreateSqlEntityDefinitionExpression (typeof (Cook));
      var nonEntity = Expression.Constant (null, typeof (Cook));

      var case1 = new SqlCaseExpression.CaseWhenPair (Expression.Constant (true), nonEntity);
      var elseCase = entity;
      var expression = new SqlCaseExpression (typeof (Cook), new[] { case1 }, elseCase);

      Assert.That (
          () => _valueRequiredVisitor.VisitSqlCaseExpression (expression),
          Throws.TypeOf<NotSupportedException> ().With.Message.EqualTo (
              "Cannot use an entity expression ('[t0]' of type 'Cook') in a place where SQL requires a single value."));
    }