【发布时间】:2018-06-27 07:51:23
【问题描述】:
我们有一个对象(XML 或 JSON),我们成功地将它映射到 DTO,插入数据库需要很长时间(5~7 分钟),所以我们通过Parallel.ForEach,但最终,我们注意到有一些数据输入不正确,比如Category 的所有项目都具有相同的名称,但其他不同的属性是 100% 正确的,在其他情况下,我们注意到一个类别中的所有数据都是相同的,尽管提供的 JSON 对象没有。
我承认它太快了,不到一分钟,但是插入错误,请看下面的代码:
JSON
[
{
"CategoryId": 1,
"CategoryName": "Drinks",
"SortOrder": 1,
"Products": [
{
"ProductId": 100,
"ProductName": "Black Tea",
"SortOrder": 1,
"Price": 5,
"Choices": []
},
{
"ProductId": 101,
"ProductName": "Turkish Coffee",
"SortOrder": 2,
"Price": 7.5,
"Choices": []
},
{
"ProductId": 102,
"ProductName": "Green Tea",
"SortOrder": 3,
"Price": 6,
"Choices": []
},
{
"ProductId": 103,
"ProductName": "Café Latte Medium",
"SortOrder": 4,
"Price": 10,
"Choices": []
},
{
"ProductId": 104,
"ProductName": "Orange Juice",
"SortOrder": 5,
"Price": 11,
"Choices": []
},
{
"ProductId": 105,
"ProductName": "Mixed Berry Juice",
"SortOrder": 6,
"Price": 12.5,
"Choices": []
}
]
},
{
"CategoryId": 1,
"CategoryName": "Meals",
"SortOrder": 1,
"Products": [
{
"ProductId": 200,
"ProductName": "Breakfast Meal",
"SortOrder": 1,
"Price": 16,
"Choices": [
{
"ChoiceId": 3000,
"ChoiceName": "Strawberry Jam",
"SortOrder": 1,
"Price": 0
},
{
"ChoiceId": 3001,
"ChoiceName": "Apricot Jam",
"SortOrder": 2,
"Price": 0
},
{
"ChoiceId": 3002,
"ChoiceName": "Orange Jam",
"SortOrder": 3,
"Price": 0
},
{
"ChoiceId": 3003,
"ChoiceName": "Café Latte",
"SortOrder": 4,
"Price": 2
}
]
},
{
"ProductId": 201,
"ProductName": "Mixed Grill",
"SortOrder": 1,
"Price": 30,
"Choices": [
{
"ChoiceId": 3004,
"ChoiceName": "Moutabal",
"SortOrder": 1,
"Price": 0
},
{
"ChoiceId": 3005,
"ChoiceName": "Mineral Water",
"SortOrder": 2,
"Price": 0
},
{
"ChoiceId": 3006,
"ChoiceName": "French Fries",
"SortOrder": 2,
"Price": 0
},
{
"ChoiceId": 3007,
"ChoiceName": "Grilled Potatoes",
"SortOrder": 2,
"Price": 0
}
]
}
]
}
]
C# 代码
Parallel.ForEach(categories, (category) =>
{
var newCreatedCategoryId = 0;
using (var connection = new SqlConnection("CONNECTION_STRING_HERE"))
{
connection.Open();
using (var command = new SqlCommand("SP_INSERT_INTO_CATEGORIES", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@P1", category.CategoryName);
command.Parameters.AddWithValue("@P2", category.SortOrder);
newCreatedCategoryId = int.Parse(command.ExecuteScalar().ToString());
command.Dispose();
}
connection.Close();
}
if (newCreatedCategoryId > 0)
{
Parallel.ForEach(category.Products, (product) =>
{
using (var connection = new SqlConnection("CONNECTION_STRING_HERE"))
{
connection.Open();
using (var command = new SqlCommand("SP_INSERT_INTO_PRODUCTS", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@P1", product.ProductName);
command.Parameters.AddWithValue("@P2", product.Price);
command.Parameters.AddWithValue("@P3", product.SortOrder);
command.Parameters.AddWithValue("@P4", newCreatedCategoryId);
command.ExecuteNonQuery();
command.Dispose();
}
connection.Close();
}
});
}
});
我查看了here,但这不是我们的问题,我们已经在使用SCOPE_IDENTITY() 来获取当前执行范围内最后生成的身份。
另一方面,即使没有TableLock,也不允许使用SqlBulkCopy插入这么多数据。
【问题讨论】:
-
你能发布实际代码吗?我问的原因是通常你必须在
SqlCommand上将CommandType指定为StoredProcedure才能传递参数,这让我认为这不是你的实际代码。 -
你的 json 有多大?你生成了多少行?
-
顺便说一句,您可能会发现使用“表值参数”会更快。
-
还有这个孩子,这就是为什么你不让人们玩 dbs 和并行 foreach :) 说真的,这不是线程安全的
-
是的,在这种情况下,数据库服务器成为瓶颈,因为它可能(请注意,这取决于服务器设置)只是阻止任何多余的调用。如果它只允许来自一个客户端的 16 个并行连接,而您用 32、64 甚至更多来轰炸它,那么超出限制的那些无论如何都会被阻止。无论如何,线程安全是这里的主要问题。先把它修好。
标签: c# sql-server parallel-processing ado.net parallel.foreach