C# EF 5.0 向 MySQL 数据库添加百万条记录需要数小时

标签 c# mysql entity-framework linq

下面是我用来向数据库添加记录的代码。我知道我每次都会调用 saveChanges() ,这很昂贵,但如果调用保存更改一次,我可能会得到重复的键异常。因此,我正在寻找任何想法来更好地提高性能,同时牢记重复记录。

using (var db = new dbEntities())
{

    for (int i = 0; i < csvCustomers.Count; i++)
    {
        var csvCustomer = csvCustomers[i];
        dbcustomer customer = new dbcustomer() { ADDRESS = csvCustomer.ADDRESS, FIRSTNAME = csvCustomer.FIRSTNAME, LASTNAME = csvCustomer.LASTNAME, PHONE = csvCustomer.PHONE, ZIPCODE = csvCustomer.ZIP };
        try
        {
            dbzipcode z = db.dbzipcodes.FirstOrDefault(x => x.ZIP == customer.ZIPCODE);
            //TODO: Handle if Zip Code not Found in DB
            if (z == null)
            {
                db.dbcustomers.Add(customer);
                throw new DbEntityValidationException("Zip code not found in database.");
            }
            customer.dbzipcode = z;
            z.dbcustomers.Add(customer);
            db.SaveChanges();
        }
    }
}

我想到的一个解决方案是批量添加数据,然后调用 db.SaveChanges() ,并且在出现异常的情况下递归地减少这些记录的批量大小。

最佳答案

与更直接的方法相比,使用 EF 插入大量记录会产生巨大的成本,但您可以考虑一些因素来显着提高性能。

首先,通过保存更改对请求进行批处理将优先于保存单个记录或尝试一次提交所有更改。如果/当批处理失败时,您将需要处理异常。 (可能一次提交一批,以完全隔离重复的行)

接下来,您可以预先缓存邮政编码,而不是每次迭代都查找它。不要加载整个实体,只需将邮政编码和 ID 缓存到内存列表中: (如果邮政编码实体仅此而已,则只需加载该实体) var zipCodes = db.dbzipcodes.Select(x => new {x.ZIPCODEID, x.ZIP}).ToList();

在批量调用中将邮政编码与客户关联时,需要额外注意,因为邮政编码最初不会被 DbContext 识别,但当第二个客户使用相同邮政编码时可能会知道被添加。

要关联邮政编码而不将其加载到 DbContext 中:

var customerZipCode = zipCodes.SingleOrDefault(x => x.ZIP = customer.ZIPCODE);
// + exists check...
var zipCode = new dbzipcode { ZIPCODEID = customerZipCode.ZIPCODEID };
db.dbzipcodes.Attach(zipCode);
customer.dbzipcode = zipCode;
// ...

如果您确实将整个邮政编码实体加载到缓存列表中,则不需要 var zipCode = new dbzipcode ...,只需附加缓存实体即可。

但是,如果在批处理中邮政编码已与 DbContext 关联,您将收到错误(无论您是否缓存实体或仅缓存 ID/代码),因此您需要首先检查 dbContext -内存邮政编码:

var customerZipCode = zipCodes.SingleOrDefault(x => x.ZIP = customer.ZIPCODE);
// + exists check...
var zipCode = db.dbzipcodes.Local.SingleOrDefault(x => x.ZIPCODEID == customerZipCode.ZIPCODEID) 
  ?? new dbzipcode { ZIPCODEID = customerZipCode.ZIPCODEID };
db.dbzipcodes.Attach(zipCode);
customer.dbzipcode = zipCode;
// ...

最后,EF 在内存中跟踪大量额外信息作为上下文,因此与批处理一起的其他考虑因素是避免在所有批处理中使用相同的 DbContext,而不是在每个批处理中打开一个 DbContext。当您添加项目并跨 DbContext 调用 SaveChanges 时,它仍在跟踪添加的每个实体。如果您执行 1000 左右的批处理,则上下文将仅跟踪这 1000 行,而不是 1000、2000、3000 等,最多 500 万行。

关于C# EF 5.0 向 MySQL 数据库添加百万条记录需要数小时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53640210/

相关文章:

c# - 如何从表中添加一些列并在另一个表中显示它们

c# - SQL 数据库的 "developer/test"模式

c# - EF需要时间来执行SP

c# - 从C#事件触发C++操作

C# : Switch between power plans

java - 如何为每个唯一值选择 1 个?

mysql - 内部连接 ​​4 个表,带 group、order by、having 子句

c# - 实体数据源在哪里

c# - 将 XmlSerializerFormat 用于 WCF 服务时如何避免换行?

javascript - 将 javascript Canvas 路径保存到数据库中的正确方法