Entity Framework Core百万级数据批处理:我用10行代码干掉SSIS!

艺帆风顺 发布于 11 小时前 3 次阅读


在处理百万级数据批处理时,传统的Entity Framework Core(EF Core)可能会遇到性能瓶颈,而SQL Server Integration Services(SSIS)虽然功能强大但配置复杂。下面介绍一种高效的EF Core批处理方法,只需10行代码即可实现类似SSIS的数据导入功能。

这种方法的核心是利用SqlBulkCopy类直接与SQL Server交互,绕过了EF Core的变更跟踪机制,大幅提高了数据导入速度。测试显示,对于100万条数据,传统的EF Core逐个插入可能需要数小时,而使用这种批量处理方法通常可以在1-2分钟内完成。

关键优势:

  1. 性能提升:比传统EF Core插入快100倍以上
  2. 代码简洁:核心功能仅需10行左右代码
  3. 事务支持:整个批量操作在一个事务中,保证数据一致性
  4. 资源高效:分批处理大数据集,内存占用低

使用时需要注意:

  • 确保数据库连接字符串有足够的权限
  • 对于超大数据集,可以调整batchSize参数
  • 该方法适用于SQL Server,其他数据库需要使用相应的批量插入API
  • 插入前确保目标表结构与实体类匹配

这种方法特别适合ETL(Extract, Transform, Load)场景,可以替代复杂的SSIS配置,同时保持代码的简洁性和可维护性。

using Microsoft.EntityFrameworkCore;using Microsoft.EntityFrameworkCore.Infrastructure;using Microsoft.EntityFrameworkCore.Storage;using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Linq;using System.Reflection;using System.Threading.Tasks;
public static class BulkExtensions{    // 批量插入方法    public static async Task BulkInsertAsyncTEntity>(this DbContext context, IEnumerable entities,         int batchSize = 2000string tableName = null)        where TEntity : class    {        if (entities == null)            throw new ArgumentNullException(nameof(entities));
        var entityType = context.Model.FindEntityType(typeof(TEntity));        if (entityType == null)            throw new ArgumentException($"Entity type {typeof(TEntity).Name} not found in the model.");
        // 获取表名        tableName = tableName ?? entityType.GetTableName();        if (string.IsNullOrEmpty(tableName))            throw new ArgumentException("Table name not found.");
        // 获取连接        var connection = context.Database.GetDbConnection();        var openedConnection = false;
        if (connection.State != ConnectionState.Open)        {            await connection.OpenAsync();            openedConnection = true;        }
        try        {            // 获取实体属性映射            var properties = entityType.GetProperties()                .Where(p => !p.IsShadowProperty() && p.PropertyInfo != null)                .ToList();
            using (var bulkCopy = new SqlBulkCopy((SqlConnection)connection,                 SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.UseInternalTransaction, null))            {                bulkCopy.BatchSize = batchSize;                bulkCopy.DestinationTableName = tableName;
                // 创建DataTable                var dataTable = new DataTable();
                // 添加列                foreach (var property in properties)                {                    var propertyType = property.ClrType;                    if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable))                        propertyType = Nullable.GetUnderlyingType(propertyType);
                    dataTable.Columns.Add(new DataColumn(property.Name, propertyType));                    bulkCopy.ColumnMappings.Add(property.Name, property.Name);                }
                // 填充DataTable                foreach (var entity in entities)                {                    var row = dataTable.NewRow();                    foreach (var property in properties)                    {                        var value = property.PropertyInfo.GetValue(entity);                        row[property.Name] = value ?? DBNull.Value;                    }                    dataTable.Rows.Add(row);                }
                // 执行批量插入                await bulkCopy.WriteToServerAsync(dataTable);            }        }        finally        {            if (openedConnection)                await connection.CloseAsync();        }    }}