【发布时间】:2020-05-14 04:25:03
【问题描述】:
我有一个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);
我从一个文件中解析了整个save,所以我不能一个一个地添加所有的集合。解析后,我有一个 Save 及其所有关联的集合,最多可添加 80k 个对象,这些对象都不存在于数据库中。
然后,当我调用dbContext.Add(save)时,处理大约需要 44 秒,RAM 使用量从 100mb 上升到大约 700mb。
然后,当我调用 dbContext.SaveChanges()(我还尝试了 EF Extensions 中的常规 BulkSaveChanges() 方法,没有显着差异)时,它需要额外的 60 秒,RAM 使用量上升到 1.3Gb。
这里发生了什么?为什么这么长和这么多的内存使用?实际上传到数据库只需要大约最后 5 秒。
PS:我也尝试过禁用更改检测但没有任何效果。
PS2:实际用法和cmets中要求的完整代码:
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();
}
[...]
}
}
【问题讨论】:
-
请显示实际代码,并显示您如何使用
BulkSaveChanges() -
欢迎来到 ORM 的奇妙世界。数据访问代码太重要且性能至关重要,无法由机器设计。
-
实体框架不是批量操作的最佳选择。也许,在这种情况下,尝试创建一个存储过程并将数据作为 json 传递?我不知道 MariaDB。您仍然可以通过 EF 调用存储过程。
-
@holger 只有一次保存。 Any 搜索非常快,因为它由 ef 转换为 sql 中的存在选择。问题不存在
-
我明白你为什么要一次性保存完整的对象图,但正如你所说,它不符合你的性能预期,所以你必须找到另一种方法。如何将文件加载到“不同”对象图中,然后分别提取不同的“表”,然后将它们存储到数据库中,这意味着您一次可以进行 1000 次部分插入。
标签: c# entity-framework entity-framework-core mariadb ef-core-3.1