当我使用我的xxxContext对象并向表发出几个Add时,那么SaveChanges()实体框架如何将其解析为SQL?它只是循环执行插入xxx 或者如果有数百行,它是否足够智能发出批量插入命令?
奖金问题:如果它没有发出批量插入有没有办法强制它,所以我的数据库性能不会被单独的插入杀死?或者批量到临时表,然后像Upsert一样合并到原始表?
答案 0 :(得分:6)
任何ORM工具的垮台都是“喋喋不休”。大多数时候这都足够了。有时它不是。
简短的回答是“不”。
这就是为什么我有时会选择IDataReader而不是EF或NHibernate等。 对于批量插入操作,我将xml发送到存储过程,然后将其粉碎并从那里批量插入/更新或合并。
所以即使我使用ORM,我创建的域库也不依赖于EF(或NHibernate)......因此我在某些情况下通过ORM会有一个“安全阀”。
答案 1 :(得分:4)
实体框架中存在若干改进的机会:
集:
yourContext.Configuration.AutoDetectChangesEnabled = false;
yourContext.Configuration.ValidateOnSaveEnabled = false;
在100个插页的包中执行SaveChanges()
...尝试使用1000并查看更改。
因为在所有这些插入期间,上下文是相同的,所以您可以每1000次插入重建上下文对象。 var yourContext = new YourContext();
在我的导入数据流程中进行此改进,从7分钟到6秒。
实际数字......在你的情况下不可能是100的1000 ...尝试并调整它。
答案 2 :(得分:3)
如果您的插入查询是ANSI SQL,或者您不关心使用您的代码库支持多重数据库,您仍然有后门从EF创建ADO.NET提供程序并执行一些原始SQL调用
https://stackoverflow.com/a/1579220/98491
我会做这样的事情
private void BulkInsert(IEnumerable<Person> Persons)
{
// use the information in the link to get your connection
DbConnection conn = ...
using (DbCommand cmd = conn.CreateCommand())
{
var sb = new StringBuilder();
sb.Append("INSERT INTO person (firstname, lastname) VALUES ");
var count = 0;
foreach(var person in persons)
{
if (count !=0) sb.Append(",");
sb.Append(GetInsertCommand(person, count++, cmd));
}
if (count > 0)
{
cmd.CommandText = sb.ToString();
cmd.ExecuteNonQuery();
}
}
if (sb.Length > 0)
ExecuteNonQuery(sb.ToString());
}
private string GetInsertCommand(Person person, int count, DbCommand cmd)
{
var firstname = "@firstname" + count.ToString();
var lastname = "@lastname" + count.ToString();
cmd.Parameters.Add(firstname, person.Firstname);
cmd.Parameters.Add(lastname, person.Firstname);
return String.Format("({0},{1})", firstname, lastname);
}
我必须承认我没有对它进行过测试,但这应该是一种快速而肮脏的方法,可以绕过一些批量插入的EF,直到批量插入是核心的一部分。
<强>更新强>
只是一个简单的想法。您是否尝试过迁移命名空间中的...方法? 也许这个是批量插入,没有考虑它但是值得一试:
private void BatchInsert(IEnumerable<Person> persons)
{
context.Persons.AddOrUpdate(persons);
}
我知道如果你定义一个像AddOrUpdate(p => p.Firstname, persons)
这样的Key列,这个方法可能会很慢,但我猜不指定它,那应该是所有插入(不保证)
答案 3 :(得分:3)
用法:
using EntityFramework.BulkInsert.Extensions;
context.BulkInsert(myEntities);
使用DbContext:
using (var ctx = GetContext())
{
using (var transactionScope = new TransactionScope())
{
// some stuff in dbcontext
ctx.BulkInsert(entities);
ctx.SaveChanges();
transactionScope.Complete();
}
}
答案 4 :(得分:0)
我担心EF不支持批量插入或更新。正如您所说,EF目前会生成一堆Insert命令并单独执行它们(但所有这些命令都包含在一个事务中)。有一些计划实施批处理,不确定最近是否有一些进展。希望在EF6,但我有点怀疑。
您可以在此discussion中阅读更多内容。
答案 5 :(得分:-1)
从.NET存储库中插入ASP .NET核心版快速方法。
public virtual void AddRangeFastAndCommit(IEnumerable<T> entities)
{
MyDbContext localContext = new MyDbContext(_context.Options);
localContext.ChangeTracker.AutoDetectChangesEnabled = false;
foreach (var entity in entities)
{
localContext.Add(entity);
}
localContext.SaveChanges();
localContext.Dispose();
}