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); 
      } 
相關問題