![](/img/trans.png)
[英]Why filtering this IEnumerable throws an NullReferenceException?
[英]Task Parallel Library throws NullReferenceException with IEnumerable
我有一个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:
引发System.AggregateException
是为了从应用程序引发多个异常。
原因
您正在以并行方式访问Connection
对象。 多个任务试图同时访问它,并在无法获取它时引发异常。 一次只有一个线程可以访问数据库连接。
创建多个线程以将数据插入DB始终不会加快速度。 (即使您设法找到任何并行方法也是如此),因为DB会为每次写入数据而锁定,并且所有数据将被顺序插入。
使用正常的插入过程,它将更快。
(一旦我弄清楚“ 1 lac”是什么),就好像您要进行批量插入 。 您可以使用SqlBulkCopy做到这一点-它旨在有效地加载SQL表。
但是 ,我看到您也希望返回ID,因此以上内容不会完全解决您的问题。 我看到您正在使用存储过程,因此是一种存储过程(假设您拥有SQL 2008及更高版本):
创建一个表值数据类型以包含要插入的数据。
CREATE TYPE [dbo].[EmployeeDataType] As Table ( ID INT, -- employee details )
更改您的存储过程以将该表值参数用作输入,并在执行插入操作时将其与OUTPUT一起使用。 例如
CREATE PROCEDURE [dbo].[EmployeeInsert] ( @EmployeeInsertParameter As [dbo].[EmployeeDataType] READONLY ) AS ... INSERT INTO Employee SELECT * FROM @EmployeeInsertParameter e OUTPUT INSERTED.*
(显然,您将命名列而不使用*)
更改您的代码以不使用Parallel.ForEach
,而是这样做:
DataTable employeeDataTable = new DataTable("EmployeeDataType"); // fill in the rows using ... insertCmd.Parameters["@EmployeeInsertParameter"].Value = employeeDataTable; ...
将存储过程执行的结果读取到List<Employee>
结论 :基本上不使用Parallel.For
进行数据库连接。 这种方式将使您正确使用一个连接(不会导致“ NPE”),并且大多数处理将在内存中完成,并且只要您具有RAM,它的数量级就会更快。
这是另一种可能的示例,但涉及更多: https : //stackoverflow.com/a/21689413/3419825
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.