当操作大量数据时,EF框架提供的数据操作方法就显得非常慢,可以利用第三方插件来提升数据操作性能。
下面的实例统一用日志表来测试,表结构如下:
数据新增
使用EF原生方法,插入一万条数据到数据库中:
using (NewsDbContext dbContext = new NewsDbContext())
{
dbContext.Database.Log += c => Console.WriteLine($"sql:{c}");
{
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
NewsLog log = new NewsLog()
{
OperateUserID = i,
OperateDate = DateTime.Now.AddDays(2),
LogContent = "我是通过普通方法第二次插入的第" + i + "条数据"
};
dbContext.NewsLogs.Add(log);
}
dbContext.SaveChanges();//新增
watch.Stop();
Console.WriteLine($"10000条数据EF常规方法新增耗时:{watch.ElapsedMilliseconds}");
}
}
执行结果:
执行了75秒
利用插件SqlBulkCopy插入一万条数据:
要使用此插件,首先要引用程序集System.Data.SqlClient
代码实例:
using (NewsDbContext dbContext = new NewsDbContext())
{
dbContext.Database.Log += c => Console.WriteLine($"sql:{c}");
{
Stopwatch watch = Stopwatch.StartNew();
//一. 构造DataTable结构并且给其赋值
//1.定义与目标表的结构一致的内存表 排除自增id列
DataTable dtSource = new DataTable();
//列名称如果和目标表设置为一样,则后面可以不用进行字段映射
dtSource.Columns.Add("OperateDate");
dtSource.Columns.Add("OperateUserID");
dtSource.Columns.Add("LogContent");
//2. 向dt中增加4W条测试数据
DataRow dr;
for (int i = 0; i < 10000; i++)
{
// 创建与dt结构相同的DataRow对象
dr = dtSource.NewRow();
dr["OperateDate"] = DateTime.Now.AddDays(1);
dr["OperateUserID"] = i;
dr["LogContent"] = "我是通过扩展方法SqlBulkCopy插入的第" + i + "条数据";
// 将dr追加到dt中
dtSource.Rows.Add(dr);
}
AddByBluckCopy(dtSource, "NewsLog");
watch.Stop();
Console.WriteLine($"10000条数据EF扩展方法新增耗时:{watch.ElapsedMilliseconds}");
}
}
/// <summary>
/// 海量数据插入方法
/// (调用该方法需要注意,DataTable中的字段名称必须和数据库中的字段名称一一对应)
/// </summary>
/// <param name="table">内存表数据</param>
/// <param name="tableName">目标数据的名称</param>
public static void AddByBluckCopy(DataTable table, string tableName)
{
string connStr = ConfigurationManager.ConnectionStrings["NewsDbContext"].ToString();//数据库链接
if (table != null && table.Rows.Count > 0)
{
using (SqlBulkCopy bulk = new SqlBulkCopy(connStr))
{
bulk.BatchSize = 1000;
bulk.BulkCopyTimeout = 100;
bulk.DestinationTableName = tableName;
//设置列映射关系
bulk.ColumnMappings.Add("OperateDate", "OperateDate");
bulk.ColumnMappings.Add("OperateUserID", "OperateUserID");
bulk.ColumnMappings.Add("LogContent", "LogContent");
bulk.WriteToServer(table);
}
}
}
数据库连接在App.config中配置
执行结果:
可以看到通过插件的方式只用了0.49秒,速度提升了很多。
数据修改和数据删除引用的第三方库是另外一个,程序集名字 Z.EntityFramework.Extensions
有关此程序集的官方文档:https://entityframework-plus.net/batch-delete
下面实例中只是用到了最基本的方法,其中还有很多其他用法,可以参考官方文档。
数据修改
使用EF原生方法修改一万条记录:
using (NewsDbContext dbContext = new NewsDbContext())
{
Stopwatch watch = Stopwatch.StartNew();
var list = dbContext.NewsLogs.Where(x => 1 == 1);
foreach (var item in list)
{
item.LogContent += "测试";
}
dbContext.SaveChanges();
watch.Stop();
Console.WriteLine($"10000条数据使用EF原生方法修改耗时:{watch.ElapsedMilliseconds}");
}
执行结果:
执行耗时4.5秒。看起来使用原生方法更新数据效率还可以,不是很慢。
因为数据库的表中一共有一万条数据,所以where条件中写了1==1,就是更新所有记录。
使用第三方插件修改一万条数据:
using (NewsDbContext dbContext = new NewsDbContext())
{
Stopwatch watch = Stopwatch.StartNew();
int count = dbContext.Set<NewsLog>().Where(u => 1 == 1).Update(x => new NewsLog()
{
LogContent = "我是修改后的值"
});
watch.Stop();
Console.WriteLine($"10000条数据使用扩展方法修改耗时:{watch.ElapsedMilliseconds}");
}
执行耗时1.7秒。
数据删除
使用EF原生方法删除一万条记录:
using (NewsDbContext dbContext = new NewsDbContext())
{
var list = dbContext.Set<NewsLog>().Where(u => u.Id <= 10000).ToList();
Stopwatch watch = Stopwatch.StartNew();
foreach (var item in list)
{
dbContext.Entry(item).State = EntityState.Deleted;
}
int count = dbContext.SaveChanges();
watch.Stop();
Console.WriteLine($"10000条数据EF常规方法删除耗时:{watch.ElapsedMilliseconds}");
}
执行结果:
普通方法删除一万条数据耗时76秒。
使用第三方插件删除一万条数据:
using (NewsDbContext dbContext = new NewsDbContext())
{
Stopwatch watch = Stopwatch.StartNew();
dbContext.NewsLogs.Where(x => x.Id <= 20000)
.Delete();
watch.Stop();
Console.WriteLine($"10000条数据使用插件方法删除耗时:{watch.ElapsedMilliseconds}");
}
使用插件删除一万条数据耗时1.6秒,速度是非常快的。