상세 컨텐츠

본문 제목

Entity Framework : 가장 빠르게 많은 양의 데이터를 인서트 하는 방법 - C# & .NET

본문

Entity Framework (엔티티 프레임워크)는 데이터를 클래스 화 하여주어 ADO를 썼을 때 보다 코딩을 쉽게 도와줍니다.  하지만 데이터의 양이 많아지게 되면 데이터를 처리하는 속도가 느려지는 것이 단점이라고 할 수 있습니다. 
 
아래의 설명을 통해 기억하기 쉽게 보통 데이터의 수가 10000개 이상이 되면 Entity Framework의 SaveChanges 나 BulkInsert를 쓰는 것보다 SQL Bulk Copy (SQL 벌크 카피)를 쓰는 게 훨씬 효율적인 것을 알게 되실 겁니다. 
 

반응형

속도 비교

아래의 차트는 인서트를 처리하는 데이터의 수에 따른 속도를 보여줍니다.  차트에서와 같이 1000개 정도가 넘어가면 그 후로는 Add Range가 확연히 느려진다는 것을 보실 수 있습니다.  Bulk Insert는 그보다 빠르게 나타납니다.

bulk insert vs add range 비교 차트
출처: https://github.com/JayKrishnareddy/BulkOperations_EFCore

 
Entity Framework의 Buk Insert 기능보다 SQL Bulk Copy가 훨씬 빠르다는 것을 아래의 차트는 보여줍니다:

Bulk Insert vs BulkCopy 비교 차트
BulkInsert vs SQLBulkCopy


설루션은?

Entity Framework는 많은 양의 데이터를 처리하는데 효율적이지 않아서 데이터의 양을 나눠서 처리를 하는 방식을 많은 개발자들이 사용하고 있습니다.  제 생각에는 데이터 하나하나를 가져와서 사용하는 데에는 Entity Framework 가 월등히 편하지만, 많은 양의 데이터를 동시에 처리하는 일에는 적당하지 않습니다. 
 
이 상황을 SQL Bult Insert 가 Entity Framework를 도와줍니다.  SQL Bulk Insert는 ADO 방식을 사용하므로 처리 속도가 빠릅니다.  사용되는 데이터 포맷은 데이터 테이블이죠. 
 

728x90

예제 코드

다음의 예제는 아래의 방식으로 데이터를 처리합니다:
 
STEP 1.  Entity Framework를 이용하여 인서트 할 데이터 (Entities)를 만듦
STEP 2.  커스텀 메서드를 이용하여 앤티티 리스트를 데이터 테이블로 변형 시킴
STEP 3.  SQL Bulk Insert를 이용하여 데이터 테이블을 서버에 인서트


STEP 1.  Entity Framework를 이용하여 인서트 할 데이터 (Entities)를 만듦

 
우선 1번의 리스트가 students라는 변수에 지정이 돼있다는 가정하에 2, 3번을 예제로 보여 드리겠습니다.


STEP 2.  커스텀 메서드를 이용하여 앤티티 리스트를 데이터 테이블로 변형 시킴

 
students는 IEnumerable 타입 (IEnumerable<Student>) 이기 때문에 아래의 static method (정적 메서드)를 이 타입에 붙일 수 있습니다:

public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
    var propsToImport = new List<PropertyDescriptor>();
    
    // 리턴될 데이터 테이블
    DataTable table = new DataTable();
    
    // 루프를 이용하여 데이터 테이블 컬럼 지정
    for (int i = 0; i < props.Count; i++)
    {
        PropertyDescriptor prop = props[i];
        Type type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
        propsToImport.Add(prop);
        table.Columns.Add(prop.Name, type);
    }

    object[] values = new object[propsToImport.Count];
    //루프를 이용하여 데이터 값이 들어간 row 를 넣음
    foreach (T item in data)
    {
        for (int i = 0; i < values.Length-1; i++)
        {
            values[i] = propsToImport[i].GetValue(item);
        }

        table.Rows.Add(values);
    }

    table.TableName = typeof(T).Name;
    return table;
}

이 메서드를 이용하는 방법은:

var dtStudents = students.ToDataTable();

이렇게 간단히 students는 dtStudents라는 데이터 테이블로 변형됩니다.


STEP 3.  SQL Bulk Insert를 이용하여 데이터 테이블을 서버에 인서트

 
이렇게 인풋 데이터가 만들어졌으면 다음의 코드 블록을 이용하여 SQL Bulk Insert를 실행합니다:

// Entity Framework의 dbcontext에 속하여 있는 Student 테이블의 Property를 Dictionary로 만듦
var tableColumnMappings = context.Student.GetProperties()
    .ToDictionary(p => p.PropertyInfo.Name, p => p.GetColumnName());

// connectionString은 변수
using (var connection = new SqlConnection(connectionString))
{
    SqlTransaction transaction = null;
    connection.Open();
    try
    {
        transaction = connection.BeginTransaction();
        using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
        {
            // destinationTableName은 변수
            sqlBulkCopy.DestinationTableName = detinationTableName;
            
            // 처음에 지정된 tableComumnMappings 변수를 이용하여 SQL 테이블 컬럼 맵핑
            foreach (var (field, column) in tableColumnMappings)
            {
                sqlBulkCopy.ColumnMappings.Add(field, column);
            }
            // dtStudents는 앞에서 리턴된 데이터 테이블
            sqlBulkCopy.WriteToServer(dtStudents);
        }
        transaction.Commit();
    }
    catch (System.Exception ex)
    {
        transaction.Rollback();
    }
}

참고로 SqlBulkCopy는 Microsoft.Data.SqlClient의 네임 스페이스에 속하여 있으므로 코드에 다음의 네임스페이스를 넣어 줍니다:

using Microsoft.Data.SqlClient;

이 코드를 사용하면 순식간에 많은 양의 데이터가 바로 인서트가 됩니다.  감사합니다.


도움이 되셨거나 즐거우셨다면 아래의 ❤️공감버튼이나 구독버튼을 눌러 주세요~  감사합니다

 

 

728x90
반응형

관련글 더보기