재우니의 블로그

Dapper (ORM) 과 EF 비교하여 WHERE 조건 검색 비교하기

 

 

Dapper는 .NET에서 사용하는 가벼운 ORM(Object-Relational Mapper)입니다. Dapper는 IQueryable 인터페이스를 직접적으로 사용하지 않고, 대신 SQL 쿼리나 저장 프로시저를 실행하여 데이터베이스에서 데이터를 검색합니다. 따라서, Dapper를 사용하여 동적 쿼리를 구성하고자 할 때는 SQL 쿼리를 문자열로 조합하거나, 더 복잡한 쿼리가 필요한 경우 Dapper의 DynamicParameters를 활용하여 조건을 동적으로 추가하는 방식을 사용할 수 있습니다.

위의 EmployeeSearchService 클래스를 Dapper를 사용하여 구현한다면, 다음과 같이 작성할 수 있습니다:

 

 

Dapper (ORM) 활용법

using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;

public class EmployeeSearchService
{
    private readonly IDbConnection _dbConnection;

    public EmployeeSearchService(IDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }

    public IEnumerable<Employee> Search(string department, int? performanceRating, string position)
    {
        var sql = new StringBuilder("SELECT * FROM Employees WHERE 1=1");
        var parameters = new DynamicParameters();

        if (!string.IsNullOrEmpty(department))
        {
            sql.Append(" AND Department = @Department");
            parameters.Add("@Department", department);
        }

        if (performanceRating.HasValue)
        {
            sql.Append(" AND PerformanceRating >= @PerformanceRating");
            parameters.Add("@PerformanceRating", performanceRating);
        }

        if (!string.IsNullOrEmpty(position))
        {
            sql.Append(" AND Position = @Position");
            parameters.Add("@Position", position);
        }

        var result = _dbConnection.Query<Employee>(sql.ToString(), parameters).ToList();
        return result;
    }
}

 

 

여기서는 StringBuilder를 사용하여 SQL 쿼리 문자열을 동적으로 구성하고 있습니다. 초기 쿼리는 모든 직원을 선택하는 SELECT * FROM Employees WHERE 1=1로 시작합니다. WHERE 1=1은 추가적인 조건이 붙기 쉽도록 만드는 일종의 트릭입니다. 그리고 각 조건이 존재할 때마다 쿼리 문자열과 DynamicParameters에 해당 조건을 추가합니다. 마지막으로, 완성된 SQL 쿼리와 파라미터를 사용하여 Dapper의 Query 메소드를 호출하고, 결과를 IEnumerable<Employee>로 반환합니다. Dapper를 사용할 때 주의할 점은 SQL 쿼리를 직접 작성한다는 것이므로, SQL 인젝션 공격에 대비하여 사용자 입력을 안전하게 처리해야 합니다. Dapper의 DynamicParameters를 사용하는 것은 이러한 위험을 줄이는 데 도움이 됩니다.

 

Dapper 자체는 IQueryable<T> 인터페이스를 직접 사용하는 방식을 지원하지 않습니다. 

 

 

 

Dapper는 주로 마이크로 ORM (Object-Relational Mapper)으로, 데이터베이스에서 데이터를 조회하거나 조작할 때 사용되는 경량화된 라이브러리입니다. 이는 SQL 쿼리를 직접 작성하고, 그 결과를 객체로 매핑하는 과정을 단순화해줍니다. 

 

IQueryable<T> 인터페이스는 LINQ (Language Integrated Query)의 일부로, 컴파일 타임에 타입 검사가 가능한 쿼리를 구성하고, 이 쿼리의 실행을 지연시킬 수 있게 해줍니다. IQueryable<T>을 사용하면, 쿼리의 구성만 정의하고, 실제 데이터베이스에 대한 쿼리 실행은 나중에 이루어집니다. 이는 Entity Framework와 같은 ORM에서 주로 사용되며, 쿼리의 결과가 필요한 시점까지 쿼리 실행을 지연시키고, 필요한 경우 쿼리를 수정하거나 추가적인 조건을 적용할 수 있게 해줍니다. 

 

Dapper를 사용할 때는 SQL 쿼리 또는 저장 프로시저를 직접 작성하고, 이를 통해 데이터를 조회합니다. 따라서, Dapper와 IQueryable<T>를 직접적으로 함께 사용하는 것은 불가능합니다. 

 

Dapper는 쿼리를 실행한 직후 데이터를 객체로 매핑하며, 이 과정에서 쿼리의 지연 실행이나 LINQ to Entities와 같은 IQueryable<T>의 기능을 이용할 수 없습니다

 

 

 

Entity Framework : IQueryable 사용법

 

이 방식은 유연하고 확장 가능하며, 사용자가 어떤 조합의 조건을 선택하든지 간에 유효한 쿼리를 생성할 수 있습니다. 추가적으로, 사용자로부터 더 많은 필터 조건을 받아야 한다면, Search 메소드에 더 많은 매개변수를 추가하고, 각 조건에 대해 유사한 방식으로 필터를 적용하면 됩니다. 이 예시는 LINQ를 사용하여 데이터베이스 쿼리를 동적으로 구성하는 방법을 보여줍니다. Entity Framework와 같은 ORMs(Object-Relational Mappers)와 함께 사용될 경우, 이 쿼리는 SQL 쿼리로 변환되어 실제 데이터베이스에서 실행됩니다.

 

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

// Employee 클래스는 데이터베이스의 직원 테이블을 나타냅니다.
public class Employee
{
    public string Department { get; set; }
    public int PerformanceRating { get; set; }
    public string Position { get; set; }
    // 여기에 더 많은 속성들이 있을 수 있습니다.
}

public class EmployeeSearchService
{
    private IQueryable<Employee> _employees;

    public EmployeeSearchService(IQueryable<Employee> employees)
    {
        _employees = employees;
    }

    public IQueryable<Employee> Search(string department, int? performanceRating, string position)
    {
        // 쿼리를 시작합니다.
        var query = _employees;

        // 각 조건을 검사하고 해당되는 경우 쿼리에 필터를 추가합니다.
        if (!string.IsNullOrEmpty(department))
        {
            query = query.Where(x => x.Department == department);
        }
        if (performanceRating.HasValue)
        {
            query = query.Where(x => x.PerformanceRating >= performanceRating.Value);
        }
        if (!string.IsNullOrEmpty(position))
        {
            query = query.Where(x => x.Position == position);
        }

        return query;
    }
}

.

 

 

 

Entity Framework  - Func<employee, bool> 타입의 람다 표현식

 

Func<Employee, bool> 타입의 람다 표현식을 조건으로 사용하는 방식은 Entity Framework (EF)에서 LINQ 쿼리를 작성할 때 유용하게 활용됩니다. Entity Framework는 LINQ를 사용하여 데이터베이스 쿼리를 작성하고 실행할 수 있게 해주는 ORM(Object-Relational Mapper)입니다. EF를 사용할 때, IQueryable<T> 인터페이스를 통해 데이터베이스 쿼리를 구성하고, 이 쿼리는 실행 시점까지 지연됩니다.

 

이 때, Func<T, bool> 타입의 람다 표현식을 사용하여 특정 조건에 따라 데이터를 필터링할 수 있습니다. 예를 들어, 주어진 조건에 따라 Employee 객체를 필터링하는 LINQ 쿼리를 작성할 수 있습니다. Where 메서드에 Func<Employee, bool> 타입의 람다 표현식을 전달함으로써, 해당 조건을 만족하는 Employee 객체만 선택됩니다. 이런 방식은 EF를 사용하는 어플리케이션에서 데이터를 조회할 때 매우 자주 사용됩니다.

 

using System;
using System.Linq;
using System.Collections.Generic;
using System.Data.Entity; // EF 6 이하에서는 System.Data.Entity를 사용합니다. EF Core에서는 Microsoft.EntityFrameworkCore를 사용합니다.

public class EmployeeContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        using (var context = new EmployeeContext())
        {
            string targetDepartment = "IT";
            int? targetRating = 4;

            // Func<Employee, bool>을 사용하여 조건 생성
            Func<Employee, bool> condition = x =>
            {
                bool matchesDepartment = string.IsNullOrEmpty(targetDepartment) || x.Department.Equals(targetDepartment);
                bool matchesRating = !targetRating.HasValue || x.PerformanceRating >= targetRating.Value;
                
                return matchesDepartment && matchesRating;
            };

            // 직원 쿼리를 위한 조건을 사용하여 데이터베이스에서 데이터를 조회
            var employees = context.Employees.Where(condition).ToList();

            // 결과 출력
            foreach (var employee in employees)
            {
                Console.WriteLine($"{employee.Firstname} {employee.Lastname}");
            }
        }
    }
}