0

我试图导入大约900K行数据并将其映射到我的新数据模型。 我的问题是,我为此导入功能构建的控制台应用程序随着时间的推移而变慢。控制台应用程序随着时间推移减慢

我监视了SQL查询并且它们都表现良好(< 5ms)。 我试图一次导入更小的块,fx 1K行。 秒表计时看起来像这样:

  • Count:100 |平均分:36
  • 计数:200 |平均分:67
  • 计数:300 |平均分:106
  • 计数:400 |平均分:145
  • 计数:500 |平均分:183
  • 计数:600 |平均分:222
  • 计数:700 |平均分:258
  • 计数:800 |平均分:299
  • 计数:900 |平均分:344
  • 计数:1000 |平均毫秒:376

当用1K行的新块重新启动应用程序时,时间是相似的。

导入数据的格式如下:

public class ImportData 
{ 
    public int Id { get; set; } 
    public int TaxpayerId { get; set; } 
    public string CustomerName { get; set; } 
    public string Email { get; set; } 
    public string PhoneNumber { get;set; } 
} 

我的数据模型的一个简单的例子是这样的:

public class Channel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Permission 
{ 
    public Guid Id { get; set; } 
    public Channel Channel { get; set; } 
    public string Recipient { get; set; } 
} 

public class Taxpayer 
{ 
    public Guid Id { get; set; } 
    public int TaxpayerId { get; set; } 
    public string Name { get; set; } 
    public List<Permission> Permissions { get; set; }   
} 

我的导入方法是这样的:

public void Import() 
{ 
    Stopwatch stopwatch = new Stopwatch(); 

    //Get import data 
    List<ImportData> importDataList = _dal.GetImportData(); 

    stopwatch.Start(); 

    for (int i = 0; i < importDataList.Count; i++) 
    { 
     ImportData importData = importDataList[i]; 

     Taxpayer taxpayer = new Taxpayer() 
     { 
      Name = importData.CustomerName, 
      TaxpayerId = importData.TaxpayerId, 
      Permissions = new List<Permission>() 
     }; 
     //Does not call SaveChanges on the context 
     CreateTaxpayer(taxpayer, false); 

     //Create permissions 
     if (!string.IsNullOrWhiteSpace(importData.Email)) 
     { 
      //Does not call SaveChanges on the context 
      CreatePermission(_channelIdEmail, importData.Email, taxpayer, PermissionLogType.PermissionRequestAccepted); 
     } 
     if (!string.IsNullOrWhiteSpace(importData.PhoneNumber)) 
     { 
      //Does not call SaveChanges on the context 
      CreatePermission(_channelIdPhoneCall, importData.PhoneNumber, taxpayer, PermissionLogType.PermissionRequestAccepted); 
      //Does not call SaveChanges on the context 
      CreatePermission(_channelIdSms, importData.PhoneNumber, taxpayer, PermissionLogType.PermissionRequestAccepted); 
     } 

     if ((i + 1) % 100 == 0) 
     { 
      Console.WriteLine("Count: " + (i + 1) + " | Avg ms: " + stopwatch.ElapsedMilliseconds/100); 
      stopwatch.Restart(); 
     } 
    } 
    _dal.SaveChanges(); 
} 

我试过以下内容:

  • 减少调用到的SaveChanges数量(只叫一次在结尾)
  • 实现多线程(没有运气) - 它似乎并没有齐头并进与实体框架

我这里没有想法。你们有任何建议来解决这个性能问题吗?

+0

请创建[MCVE。你显示的代码没有明显的问题。我会怀疑你做了一个Linq(无论是实体还是对象)查询某处,随着你添加项目到一个集合逐渐变慢。 – CodeCaster

+1

*不要*使用ORM。使用SqlBulkCopy。 ORM不适用于批量操作,就像使用镊子移动整车的石块。您最终将跟踪*所有*记录,为每个*插入发送单独的INSERT请求。您*可以*禁用更改跟踪并添加一个扩展,以将批处理添加到EF,以便多个请求一起发送,但是通过SqlBulkCopy使用EF仍然没有任何收获。 –

+0

[SqlBulkCopy和实体框架]的可能重复(http://stackoverflow.com/questions/2553545/sqlbulkcopy-and-entity-framework) –

回答

0

你为什么不使用BulkCopy,该代码将需要修改为特定的表&列,但希望你的想法:

using (var bulkCopy = new SqlBulkCopy(_DbContext.Database.Connection.ConnectionString, SqlBulkCopyOptions.TableLock)) 
      { 
       bulkCopy.BulkCopyTimeout = 1200; // 20 minutes 
       bulkCopy.BatchSize = 10000; 
       bulkCopy.DestinationTableName = "TaxPayer"; 

       var table = new DataTable(); 
       var props = TypeDescriptor.GetProperties(typeof(TaxPayer))          
        //Dirty hack to make sure we only have system data types          
        //i.e. filter out the relationships/collections 
        .Cast<PropertyDescriptor>() 
        .Where(propertyInfo => propertyInfo.PropertyType.Namespace.Equals("System")) 
        .ToArray(); 
       foreach (var propertyInfo in props) 
       { 
        bulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name); 
        table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType); 
       } 

       // need to amend next line to account for the correct number of columns 
       var values = new object[props.Length + 1]; 
       foreach (var item in importDataList) 
       { 
        for (var i = 0; i < values.Length - 1; i++) 
        { 
         ///TODO: Decide which columns need including 
         values[i] = props[i].GetValue(item); 
        } 
        table.Rows.Add(values); 
       } 

       bulkCopy.WriteToServer(table); 
      } 
相关问题