KeiStory

데이터를 동적 조건으로 쿼리하기

 

데이터를 조회할때 조회 조건을 동적으로 받아 조회하는 방법입니다.

Expressions 을 이용해 처리가 가능하면 문자열인 경우는 숫자 처럼 비교가 불가능 하므로 다르게 처리해야합니다.

조건은 conditions 에 들어가게 되면 여기에는 조건필드와 값 , 비교연산자가 들어가게됩니다.

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);
    }
}
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band