EF Core 慢速批量插入(约 80k 行)

Pin*_*nx0 6 c# entity-framework mariadb entity-framework-core ef-core-3.1

我有一个Save对象,它有多个关联的集合。对象的总大小如下:

在此输入图像描述

对象之间的关系可以从该映射中推断出来,并且在数据库中似乎得到了正确的表示。查询也很好。

modelBuilder.Entity<Save>().HasKey(c => c.SaveId).HasAnnotation("DatabaseGenerated",DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Save>().HasMany(c => c.Families).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Countries).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Provinces).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Save>().HasMany(c => c.Pops).WithOne(x => x.Save).HasForeignKey(x => x.SaveId);
modelBuilder.Entity<Country>().HasOne(c => c.Save);
modelBuilder.Entity<Country>().HasMany(c => c.Technologies).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.CountryId});
modelBuilder.Entity<Country>().HasMany(c => c.Players).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.CountryId});
modelBuilder.Entity<Country>().HasMany(c => c.Families).WithOne(x => x.Country).HasForeignKey(x => new {x.SaveId, x.OwnerId});
modelBuilder.Entity<Country>().HasMany(c => c.Provinces).WithOne(x => x.Owner);
modelBuilder.Entity<Country>().HasKey(c => new { c.SaveId, c.CountryId });
modelBuilder.Entity<Family>().HasKey(c => new { c.SaveId, c.FamilyId });
modelBuilder.Entity<Family>().HasOne(c => c.Save);
modelBuilder.Entity<CountryPlayer>().HasKey(c => new { c.SaveId, c.CountryId, c.PlayerName });
modelBuilder.Entity<CountryPlayer>().HasOne(c => c.Country);
modelBuilder.Entity<CountryPlayer>().Property(c => c.PlayerName).HasMaxLength(100);
modelBuilder.Entity<CountryTechnology>().HasKey(c => new { c.SaveId, c.CountryId, c.Type });
modelBuilder.Entity<CountryTechnology>().HasOne(c => c.Country);
modelBuilder.Entity<Province>().HasKey(c => new { c.SaveId, c.ProvinceId });
modelBuilder.Entity<Province>().HasMany(c => c.Pops).WithOne(x => x.Province);
modelBuilder.Entity<Province>().HasOne(c => c.Save);
modelBuilder.Entity<Population>().HasKey(c => new { c.SaveId, c.PopId });
modelBuilder.Entity<Population>().HasOne(c => c.Province);
modelBuilder.Entity<Population>().HasOne(c => c.Save);
Run Code Online (Sandbox Code Playgroud)

我从文件中解析整个内容save,因此无法一一添加所有集合。解析之后,我得到了 aSave及其所有关联的集合,添加了多达 80k 个对象,其中没有一个对象存在于数据库中。

然后,当我调用时dbContext.Add(save),处理时间大约为 44 秒,RAM 使用量从 100mb 上升到大约 700mb。

然后,当我调用时dbContext.SaveChanges()(我也尝试了 EF 扩展中的常规BulkSaveChanges()方法,没有显着差异),它需要额外的 60 秒,RAM 使用量高达 1.3Gb。

这里发生了什么?为什么这么长的时间和这么多的内存使用量?实际上传到数据库只需要最后5秒左右。

PS:我也尝试禁用更改检测,但没有效果。

PS2:实际用法和评论中要求的完整代码:

public class HomeController : Controller
{
    private readonly ImperatorContext _db;

    public HomeController(ImperatorContext db)
    {
        _db = db;
    }

    [HttpPost]
    [RequestSizeLimit(200000000)]
    public async Task<IActionResult> UploadSave(List<IFormFile> files)
    {
        [...]
        await using (var stream = new FileStream(filePath, FileMode.Open))
        {
            var save = ParadoxParser.Parse(stream, new SaveParser());
            if (_db.Saves.Any(s => s.SaveKey == save.SaveKey))
            {
                 response = "The save you uploaded already exists in the database.";
            }
            else
            {
                 _db.Saves.Add(save);
            }
            _db.BulkSaveChanges();
        }
        [...]
    }

}
Run Code Online (Sandbox Code Playgroud)

Tra*_*ain 0

编辑:1.确保问题不是数据库。

执行你自己的命令看看它运行的速度有多快。

  1. 通过为每个工作单元使用新的上下文来保持活动上下文图较小,同时尝试关闭 AutoDetechChangesEnabled

3.将多个命令一起批处理

这是一篇关于实体框架和缓慢批量插入的好文章