我有一个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:
答案 0 :(得分:1)
System.AggregateException
,因为可能会从应用程序中引发多个异常。
原因
您正在以并行模式访问Connection
对象。多个任务正在尝试同时访问它,并在无法获取它时引发异常。一时只有一个线程可以访问数据库连接
创建多个线程以将数据插入到数据库中无论如何都不会加快速度。 (即使您设法找到任何并行方法),因为DB会在每次写入数据时锁定,并且所有数据都将按顺序插入。
使用正常的插入过程,它会更快。
答案 1 :(得分:0)
(一旦我弄清楚&#34; 1 lac&#34;是什么)看起来你想做一个批量插入。您可以使用SqlBulkCopy执行此操作 - 它旨在有效地加载SQL表。
然而,我看到你也想要ids回来,所以上面不会让你一路走来。我看到你正在使用存储过程这样做的一种方式(假设你有SQL 2008及更高版本):
创建一个表值数据类型以包含要插入的数据。
CREATE TYPE [dbo].[EmployeeDataType] As Table
(
ID INT,
-- employee details
)
更改存储过程以使用此表值参数作为输入,当它执行插入时,它会执行和输出。 e.g。
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
进行数据库连接。这种方式可以让你正确使用一个连接(不会导致&#34; NPE&#34;)并且大部分处理将在内存中完成,只要你有RAM,它就会快几个数量级。 / p>
以下是另一个示例可能的方法,但更多涉及:https://stackoverflow.com/a/21689413/3419825