任务并行库使用IEnumerable抛出NullReferenceException

时间:2016-02-03 17:47:18

标签: c# .net task-parallel-library parallel.foreach

我有一个IEnumerable实体,它拥有大约10万条记录。我想执行Parallel.ForEach来插入这些数据。

在这里说我所拥有的课程:Employee.cs

SqlConneciton conn = base.GetConnection();
conn.open();
IEnumerable<Employee> employeeList = GetListofEmployeesFromDB();
Parallel.ForEach(employeeList
, employee =>
{
    employee.add(conn, sqlTransaction);
});

Empployee.cs 
{
      public void add(SqlConnection conn, SqlTransaction sqlTransaction)
      {
             using (SqlCommand insertCmd = new SqlCommand("EmployeeInsert", conn))
             {
                 insertCmd.CommandType = CommandType.StoredProcedure;
                 insertCmd.Transaction = transaction;
                 insertCmd.Parameters["@Name"].Value = this.Name;
                 insertCmd.ExecuteNonQuery();
                 this.id = (int)insertCmd.Parameters["@Id"].Value;
             }
      }
}

随着数据的插入,我发现NPE位于:

this.id = (int)insertCmd.Parameters["@Id"].Value;

不确定我是否遗漏了某些东西。这是我看到的例外。

System.AggregateException was unhandled
  Message=AggregateException_ctor_DefaultMessage
  Source=System.Threading
  StackTrace:
       at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
       at System.Threading.Tasks.Task.Wait()
       at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner`1 source, ParallelOptions parallelOptions, Action`1 simpleBody, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
       at System.Threading.Tasks.Parallel.ForEach[TSource](Partitioner`1 source, ParallelOptions parallelOptions, Action`1 body)
        :
        :
        :
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart(Object obj)
  InnerException: System.NullReferenceException
       Message=Object reference not set to an instance of an object.
       Source=Jobvite.Library
       StackTrace:            
            :
            :
            :
            at System.Threading.Tasks.Parallel.<>c__DisplayClass32`2.<PartitionerForEachWorker>b__30()
            at System.Threading.Tasks.Task.InnerInvoke()
            at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
            at System.Threading.Tasks.Task.<>c__DisplayClass3.<ExecuteSelfReplicating>b__2(Object )
       InnerException: 

2 个答案:

答案 0 :(得分:1)

引发了{p> System.AggregateException,因为可能会从应用程序中引发多个异常。

原因
您正在以并行模式访问Connection对象。多个任务正在尝试同时访问它,并在无法获取它时引发异常。一时只有一个线程可以访问数据库连接 创建多个线程以将数据插入到数据库中无论如何都不会加快速度。 (即使您设法找到任何并行方法),因为DB会在每次写入数据时锁定,并且所有数据都将按顺序插入。

使用正常的插入过程,它会更快。

答案 1 :(得分:0)

(一旦我弄清楚&#34; 1 lac&#34;是什么)看起来你想做一个批量插入。您可以使用SqlBulkCopy执行此操作 - 它旨在有效地加载SQL表。

然而,我看到你也想要ids回来,所以上面不会让你一路走来。我看到你正在使用存储过程这样做的一种方式(假设你有SQL 2008及更高版本):

  1. 创建一个表值数据类型以包含要插入的数据。

    CREATE TYPE [dbo].[EmployeeDataType] As Table
    (
       ID INT,
       -- employee details
    )
    
  2. 更改存储过程以使用此表值参数作为输入,当它执行插入时,它会执行和输出。 e.g。

    CREATE PROCEDURE [dbo].[EmployeeInsert]
    (
       @EmployeeInsertParameter As [dbo].[EmployeeDataType] READONLY
    )
    AS
    ...
    
    INSERT INTO Employee
    SELECT * FROM @EmployeeInsertParameter e
    OUTPUT INSERTED.*
    
  3. (显然你会命名列而不是使用*)

    1. 将您的代码更改为而不是使用Parallel.ForEach,而是执行此操作:

      DataTable employeeDataTable = new DataTable("EmployeeDataType");
      // fill in the rows using
      ...
      insertCmd.Parameters["@EmployeeInsertParameter"].Value = employeeDataTable;
      ...
      
    2. 将存储过程执行的结果读入List<Employee>

    3. 结论:基本上不使用Parallel.For进行数据库连接。这种方式可以让你正确使用一个连接(不会导致&#34; NPE&#34;)并且大部分处理将在内存中完成,只要你有RAM,它就会快几个数量级。 / p>

      以下是另一个示例可能的方法,但更多涉及:https://stackoverflow.com/a/21689413/3419825