为什么Entity Framework在一个SaveChanges()中添加多个项目这么慢?

And*_*son 4 c# entity-framework entity-framework-6

这是前一个问题的后续跟进,我试图找出代码运行缓慢的主要原因.我想我已经把它缩小到下面的一个最小例子.我有一个基本的数据库结构如下:

public class Foo
{
    public int Id { get; set; }
    public string Bar { get; set; }
}

public class FooContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我有一个Foo对象列表,并希望将它们添加到数据库,建议的方法是使用AddRange().但是我注意到它花了很长时间,并且受到集合中项目数量的影响很小,即使是200这样的少量.所以我手动编写了它,并且中提琴,它运行得更快!

class Program
{
    static void Main(string[] args)
    {
        var foos = Enumerable.Range(0, 200).Select(index => new Foo { Bar = index.ToString() });

        // Make sure the timing doesn't include the first connection
        using (var context = new FooContext())
        {
            context.Database.Connection.Open();
        }

        var s1 = Stopwatch.StartNew();
        using (var context = new FooContext())
        {
            context.Foos.AddRange(foos);
            context.SaveChanges();
        }
        s1.Stop();

        var s2 = Stopwatch.StartNew();
        using (var context = new FooContext())
        {
            // Ignore the lack of sanitization, this is for demonstration purposes
            var query = string.Join(";\n", foos.Select(f => "INSERT INTO Foos ([Bar]) VALUES (" + f.Bar + ")"));
            context.Database.ExecuteSqlCommand(query);
        }
        s2.Stop();

        Console.WriteLine("Normal way: {0}", s1.Elapsed);
        Console.WriteLine("Hard way  : {0}", s2.Elapsed);
        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

我最初的想法是,实体框架可能正在为每个条目使用单独的事务,但是记录SQL显示情况并非如此.那为什么执行时间会有这么大差异呢?

Sal*_*Sal 5

在对您的问题进行一些研究时,我发现了这篇具有启发性的文章:http://www.codinghelmet.com/? path =howto/bulk-insert

这是一个引用:

插入的每个对象都需要两个SQL语句 - 一个用于插入记录,另外一个用于获取新记录的标识

插入多个记录时会出现问题.每个记录一次插入一个事实会加剧一个问题(但是这已经超出了你的问题的上下文,因为你已经在逐个插入测试).因此,如果您要插入200条记录,则会逐个执行400条sql语句.

所以根据我的理解,EF根本不是为批量插入而构建的.即使它像插入200条记录一样简单.这对我来说似乎是一个很大的失望.

我开始思考,"那么EF无论如何都是好事.它甚至不能插入几条记录".那么我会在两个方面给EF道具:

  1. 选择查询:编写查询并快速将数据导入应用程序非常容易.
  2. 简化复杂记录的插入.如果您曾经拥有一张包含大量外键的表,并且您尝试在一次交易中插入所有链接的记录,那么您就知道我在说什么.值得庆幸的是,EF按顺序插入每条记录,并在一次交易中为您链接所有相关记录.但如上所述,这需要付出代价.

所以简单地说,似乎,如果你有一个需要插入一堆记录的操作,最好使用SqlBulkCopy.这可以在几秒钟内插入数千条记录.

我知道这可能不是你想听到的答案,因为相信我,因为我使用了很多EF,所以它也让我感到不安,但是我没有看到任何解决方法