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 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(); 
} 

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; }   
} 

私のインポート方法は次のようになります

私は以下を試しました:

  • (運なし)マルチスレッド実装
  • (唯一の終わりに一度と呼ばれる)のSaveChangesへの呼び出しの数を減らすこと - 私は、エンティティフレームワーク

と手をつないで行くようには見えませんここでアイディアが不足しています。このパフォーマンスの問題を解決するための提案はありますか?

+0

を[MCVE]を作成してください。表示するコードには明らかな問題はありません。私はあなたがコレクションにアイテムを追加するにつれ、徐々に遅くなるLinq(エンティティまたはオブジェクトのいずれか)クエリをどこかで行うと思われます。 – CodeCaster

+1

* ORMは使用しないでください。 SqlBulkCopyを使用します。 ORMはバッチ操作のためのものではなく、ピンセットを使って岩石のトラックを移動させるのと同じです。 *すべての*レコードを追跡して、*挿入ごとに個別のINSERT要求を送信します。変更トラッキングを無効にしたり、EFにバッチ処理を追加して複数の要求をまとめて送信する拡張機能を追加することもできますが、SqlBulkCopyでEFを使用しても何も得られません。 –

+0

[SqlBulkCopyとEntity Framework]の可能な複製(http://stackoverflow.com/questions/2553545/sqlbulkcopy-and-entity-framework) –

答えて

0

あなたがバルク・コピーを使用していないのはなぜ、このコードは、あなたの特定のテーブルに対して&列を修正が必要になりますが、うまくいけばあなたのアイデアを得る:

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); 
      } 
関連する問題