使用带有块的EF更新数据库运行速度非常慢

时间:2016-11-15 05:22:28

标签: c# sql entity-framework linq

我在StackOverflow找到了这段代码:

public static class EntityFrameworkUtil
{
    public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
    {
        return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
    }

    public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
    {
        int chunkNumber = 0;
        while (true)
        {
            var query = (chunkNumber == 0)
                ? queryable
                : queryable.Skip(chunkNumber * chunkSize);
            var chunk = query.Take(chunkSize).ToArray();
            if (chunk.Length == 0)
                yield break;
            yield return chunk;
            chunkNumber++;
        }
    }
} 

我这样用:

int counter = 0;
    foreach (var chunk in _sdb.Posts.OrderBy(c => c.Id).QueryChunksOfSize(100))
        {
            foreach(var post in chunk)
            {
                post.Floor= CorrectFloor(post);
                post.RealAge= AgeCalculator(post);
                post.Area= TerasCheck(post);

                _sdb.Entry(post).State = System.Data.Entity.EntityState.Modified;
            counter++;
            }


        _sdb.SaveChanges();
        Console.WriteLine("SaveChanges worked, counter : " + counter);

    }

此代码应该更新300000行。代码一开始工作得很快,但现在它的工作速度非常慢,并且它在第30000行。你能告诉我为什么这段代码运行缓慢,我怎样才能让它更快?感谢。

1 个答案:

答案 0 :(得分:1)

我建议如下:

  1. 使用IQueryable<T>代替IEnumerable<T>。使用IEnumerable<T>将导致对数据库进行的许多查询将降低整体性能。此处OrderBy子句也没有意义,因为您想要更新所有帖子。
  2. 调用SaveChanges一次。您的示例将每100个元素提交更改。
  3. 检查您的DataContext配置以避免LazyLoading(包括您要加载的属性)和跟踪更改(您也可以按照建议调用AsNoTracking()):

    this.Configuration.LazyLoadingEnabled = false; this.Configuration.AutoDetectChangesEnabled = false; this.Configuration.ProxyCreationEnabled = false;

  4. 如果此过程需要很长时间才能执行,请确保在async函数中执行。总结一下,就像这样:

    int counter = 0;
    foreach (var post in _sdb.Posts.Include("xxx"))
    {
        post.Floor = CorrectFloor(post);
        post.RealAge = AgeCalculator(post);
        post.Area = TerasCheck(post);
    
        _sdb.Entry(post).State = System.Data.Entity.EntityState.Modified;
        counter++;
    }
    
    _sdb.SaveChanges();
    Console.WriteLine("SaveChanges worked, counter : " + counter);