C#EF 5.0向MySQL DB添加数百万条记录需要花费数小时

时间:2018-12-05 20:26:49

标签: 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(),如果出现异常,则递归地减少这些记录的批处理大小。

1 个答案:

答案 0 :(得分:1)

与更直接的方法相比,使用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万行。