我有 5 个实体:
public class Album
{
public int Id { get; set; }
public string Title { get; set; }
public virtual List<AlbumArtist> AlbumArtists { get; set; }
public virtual List<Artist> Artists { get; set; }
public virtual List<Genre> Genres { get; set; }
public virtual List<Song> Songs { get; set; }
}
public class AlbumArtist
{
public int Id { get; set; }
public string Title { get; set; }
public virtual List<Album> Albums { get; set; }
public virtual List<Artist> Artists { get; set; }
public virtual List<Genre> Genres { get; set; }
public virtual List<Song> Songs { get; set; }
}
public class Artist
{
public int Id { get; set; }
public string Title { get; set; }
public virtual List<AlbumArtist> AlbumArtists { get; set; }
public virtual List<Album> Albums { get; set; }
public virtual List<Genre> Genres { get; set; }
public virtual List<Song> Songs { get; set; }
}
public class Genre
{
public int Id { get; set; }
public string Title { get; set; }
public virtual List<AlbumArtist> AlbumArtists { get; set; }
public virtual List<Album> Albums { get; set; }
public virtual List<Artist> Artists { get; set; }
public virtual List<Song> Songs { get; set; }
}
public class Song
{
public int Id { get; set; }
public string Title { get; set; }
public virtual List<AlbumArtist> AlbumArtists { get; set; }
public virtual List<Album> Albums { get; set; }
public virtual List<Artist> Artists { get; set; }
public virtual List<Genre> Genres { get; set; }
}
如您所见,有很多多对多关系。我填充我的实体,然后尝试以这种方式将它们保存到 DbContext:
_albumArtists.ForEach(delegate(AlbumArtist albumArtist)
{
if (albumArtist.Id == 0)
{
_dbContext.Entry(entity).State = EntityState.Added;
_dbContext.SaveChanges();
}
else
{
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
}
});
...
或者以那种方式:
_albumArtists.ForEach(delegate(AlbumArtist albumArtist)
{
if (albumArtist.Id == 0)
{
_dbContext.Entry(entity).State = EntityState.Added;
}
else
{
_dbContext.AlbumArtists.State = EntityState.Modified;
}
});
_dbContext.SaveChanges();
...
将我的实体保存到 DbContext 需要很长时间。我什至尝试执行以下操作:
Configuration.AutoDetectChangesEnabled = false;
但这并没有帮助。顺便说一句,大约有 17 000 首歌曲和 1 700 张专辑。
怎么了???
请帮忙!
附言
这是我的完整代码:https://github.com/vjacheslavravdin/PsyTrance/blob/master/PsyTrance/Program.cs 也许您可以建议如何简化它。
谢谢!
最佳答案
首先澄清几点:
对于基于批处理的操作,EF 并不比其他方法慢很多。在我的测试中,使用原始 SQL 命令可能会提高 50%,使用 SQL 批量复制可能会快 10 倍,但作为一般规则,EF 并不比比较方法慢多少(尽管通常被认为非常慢)。对于大多数应用程序,即使在批处理场景中进行了正确的调整,EF 也会给出合适的性能数字。 (在这里查看我的文章:http://blog.staticvoid.co.nz/2012/3/24/entity_framework_comparative_performance 和 http://blog.staticvoid.co.nz/2012/8/17/mssql_and_large_insert_statements)
由于 EF 更改跟踪的方式,它有可能远远超过大多数人编写基于 SqlCommand 的插入语句的性能(有很多与查询计划、往返和事务有关的细节,使它变得漂亮难以编写最佳执行的批量插入语句)。我已在此处 ( http://entityframework.codeplex.com/discussions/377636 ) 向 EF 提出了这些补充,但尚未实现。
您决定关闭自动检测更改的决定是完全正确的,每个启用检测更改的 .Add 或 .Attach 操作都会枚举跟踪图,因此如果您要在同一上下文中添加 17k 个添加项,则需要枚举该图在总共 17000 + 16999 + ...+ 2 + 1 = 144,500,000 个实体中重复了 17000 次,难怪要花这么长时间,对吗? (在这里查看我的文章:http://blog.staticvoid.co.nz/2012/5/7/entityframework_performance_and_autodetectchanges)
保存更改总是需要枚举跟踪图(它在内部调用检测更改),因此您的第一种方法会很慢,因为它实际上将执行与上述相同数量的跟踪调用。
第二种方法要好得多,但它仍然有一个相当大的缺陷,我认为这是双重的,首先,当你去保存更改时,图表真的很大(更大的图表有成倍增加的跟踪时间),其次,它会一次保存整个图形会占用大量内存,特别是考虑到 EF 存储每个实体的两个副本。
一个更好的方法是以 block 的形式保存你的图表。一些
//With Auto detect changes off.
foreach(var batch in batches)//keep batch size below 1000 items, play around with the numbers a little
{
using(var ctx = new MyContext())//make sure you create a new context per batch.
{
foreach(var entity in batch){
ctx.Entities.Add(entity);
}
ctx.SaveChanges();
}
}
我希望您应该以 17-30 岁左右为目标来完成所有 17k 行。
通过使用原始 SQL 命令执行此操作,您可以将其缩短到 12-20 秒左右;
通过批量复制的重新实现,您可能会将其缩短到 2-5 秒
关于c# - Entity Framework 5 性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18645557/