随着时间的推移,控制台应用程

时间:2017-02-10 09:57:20

标签: asp.net sql-server performance entity-framework console-application

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

我监控了SQL查询,它们都运行良好(<5ms)。 我试图一次导入较小的块,fx 1K行。 秒表计时如下:

  • 数:100 |平均时间:36
  • 数:200 |平均时间:67
  • 数:300 |平均ms:106
  • 数:400 |平均毫秒:145
  • 数:500 |平均ms:183
  • 数:600 |平均时间:222
  • 数:700 |平均毫秒数:258
  • 数:800 |平均时间:299
  • 数:900 |平均ms:344
  • 数:1000 |平均ms: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的调用次数(最后只调用一次)
  • 实现多线程(没有运气) - 它似乎与实体框架并不相符

我的想法已经不多了。你们有什么建议来解决这个性能问题吗?

1 个答案:

答案 0 :(得分:0)

为什么不使用BulkCopy,此代码需要修改您的特定表格&amp;专栏,但希望你明白这个想法:

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