본문 바로가기
카테고리 없음

문자열 컬럼에 대해서도 `>` 또는 `<`와 같은 비교 연

by keisoft 2024. 8. 22.

문자열 컬럼에 대해서도 `>` 또는 `<`와 같은 비교 연산을 할 수 있습니다. 문자열 비교는 기본적으로 알파벳 순서(사전적 순서)에 따라 이루어집니다. 예를 들어 `"apple" < "banana"`는 `true`로 평가됩니다. 이러한 비교는 `string.Compare` 메서드를 사용하거나 기본 `Expression.GreaterThan` 또는 `Expression.LessThan`을 이용할 수 있습니다.

다음은 문자열 컬럼을 포함한 비교 예제를 보여줍니다.

### 예시 코드:

```csharp
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

public class Program
{
    public class Item
    {
        public int Value1 { get; set; }
        public int Value2 { get; set; }
        public string Name { get; set; }  // 문자열 컬럼 추가
    }

    public static void Main()
    {
        var items = new List<Item>
        {
            new Item { Value1 = 5, Value2 = 10, Name = "apple" },
            new Item { Value1 = 10, Value2 = 15, Name = "banana" },
            new Item { Value1 = 20, Value2 = 25, Name = "cherry" }
        };

        // 각 컬럼에 대해 비교할 조건 (문자열 포함)
        var conditions = new List<(string PropertyName, string Operator, object Value)>
        {
            ("Value1", ">", 5),
            ("Value2", "<=", 20),
            ("Name", "<", "cherry")  // 문자열 비교
        };

        // 조건을 결합한 Expression을 생성
        var predicate = BuildDynamicQuery<Item>(conditions);

        // LINQ 쿼리 실행
        var filteredItems = items.AsQueryable().Where(predicate).ToList();

        // 필터링된 결과 출력
        foreach (var item in filteredItems)
        {
            Console.WriteLine($"Value1: {item.Value1}, Value2: {item.Value2}, Name: {item.Name}");
        }
    }

    public static Expression<Func<T, bool>> BuildDynamicQuery<T>(List<(string PropertyName, string Operator, object Value)> conditions)
    {
        // 매개 변수
        var parameter = Expression.Parameter(typeof(T), "x");

        // 조건들을 결합한 Expression을 생성
        Expression body = null;

        foreach (var condition in conditions)
        {
            var member = Expression.Property(parameter, condition.PropertyName);
            var constant = Expression.Constant(condition.Value);

            Expression comparison;

            // 문자열인지 확인
            if (member.Type == typeof(string))
            {
                // 문자열 비교 연산을 처리하기 위해 string.Compare 메서드 사용
                var compareMethod = typeof(string).GetMethod("Compare", new[] { typeof(string), typeof(string) });
                var compareExpression = Expression.Call(null, compareMethod, member, constant);

                comparison = condition.Operator switch
                {
                    "=" => Expression.Equal(member, constant),
                    ">" => Expression.GreaterThan(compareExpression, Expression.Constant(0)),
                    "<" => Expression.LessThan(compareExpression, Expression.Constant(0)),
                    ">=" => Expression.GreaterThanOrEqual(compareExpression, Expression.Constant(0)),
                    "<=" => Expression.LessThanOrEqual(compareExpression, Expression.Constant(0)),
                    "!=" => Expression.NotEqual(member, constant),
                    _ => throw new NotSupportedException($"Unsupported operator: {condition.Operator}")
                };
            }
            else
            {
                // 문자열이 아닌 경우 기본 연산자 사용
                comparison = condition.Operator switch
                {
                    "=" => Expression.Equal(member, constant),
                    ">" => Expression.GreaterThan(member, constant),
                    "<" => Expression.LessThan(member, constant),
                    ">=" => Expression.GreaterThanOrEqual(member, constant),
                    "<=" => Expression.LessThanOrEqual(member, constant),
                    "!=" => Expression.NotEqual(member, constant),
                    _ => throw new NotSupportedException($"Unsupported operator: {condition.Operator}")
                };
            }

            // 조건들을 And로 결합
            body = body == null ? comparison : Expression.AndAlso(body, comparison);
        }

        // 최종 Expression 생성
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }
}
```

### 코드 설명:

1. **`BuildDynamicQuery<T>` 함수에서 문자열 처리 추가**:
   - 문자열 컬럼을 비교할 때 `string.Compare` 메서드를 사용합니다. `string.Compare`는 두 문자열을 비교하여 음수, 0, 양수를 반환하는 메서드입니다. 이를 통해 `>` 또는 `<` 같은 비교가 가능합니다.
   - `Expression.Call`을 사용하여 `string.Compare` 메서드를 호출하는 식을 만듭니다. 그리고 반환된 값이 0보다 크거나 작은지를 비교하여 `GreaterThan` 또는 `LessThan` 등의 연산을 수행합니다.

2. **문자열 비교 예시**:
   - 예를 들어 `"Name < 'cherry'"` 조건은 사전적 순서로 `"Name"`이 `"cherry"`보다 앞에 있는 문자열을 필터링합니다.
   - 이 외에도 `>` 연산자 등 다른 비교 연산자도 유사한 방식으로 처리됩니다.

3. **다양한 데이터 타입 처리**:
   - 코드에서 문자열인지 여부를 확인한 후 해당 데이터 타입에 맞는 비교식을 생성합니다. 숫자 및 기타 기본 타입에 대해서는 기존처럼 `Expression.GreaterThan` 등으로 처리하고, 문자열에 대해서만 `string.Compare`를 사용합니다.

### 실행 결과:

이 예제에서는 `Value1 > 5`, `Value2 <= 20`, 그리고 `Name < "cherry"` 조건이 모두 만족하는 항목들만 필터링됩니다.

### 결론:

이 방식으로 숫자 컬럼뿐만 아니라 문자열 컬럼에 대해서도 동적으로 부등호 비교를 수행할 수 있습니다. `Expression` 트리를 활용하여 각 데이터 타입에 맞는 비교식을 생성하는 유연한 방법을 사용할 수 있습니다.