
[英]C# & SQL Server stored procedure multiple insert actions, influence?
[英]Multiple async tasks causing duplicates on SQL Server insert stored procedure
我有一个多线程应用程序,它遍历队列并获取数据并将这些数据发送到存储过程,然后将其插入我的表中。 问题是有时会在完全相同的时间插入此数据,这会导致插入重复的行。 现在这些行确实具有作为 id 的主键,但是所有其他列都是完全相同的数据。
这是我的循环,最多产生 20 个线程。
var task = new Task();
foreach(job in jobList)
{
task = Task.Run(() => ProcessJobs(job));
}
Task.WaitAll(task);
每个线程读取自己单独的队列,然后处理每条消息并将其添加到 HashSet 以确保没有重复
private async Task<string> ProcessJobs(Job job)
{
var messageData = getMessageFromQueue(message);
HashSet<UserInfo> list = new HashSet<UserInfo>();
foreach(var message in messageData)
{
list.Add(BuildMessage(message));
}
InsertIntoDB(list);
}
public HashSet<UserInfo> BuildMessage(MessageData messageData)
{
return new UserInfo
{
UserName = messageData.UserName,
Address = messageData.Address,
AccountType = messageData.Campaign?.AccountType == "G" ? "Type1" :"Type2",
AccountNumber = messageData.AccountList != null ? messageData.AccountList[0].ToString() : string.Empty.
}
}
public struct UserInfo
{
public string UserName { get; set; }
public string Address { get; set; }
public string AccountType { get; set; }
public string AccountNumber { get; set; }
}
每条消息都被处理并作为表值参数发送到数据库以插入语句
public async Task<int> InsertIntoDB(HashSet<UserInfo> list)
{
// First convert the hashset to a dataTable
var dataTable = list.ToDatatable();
// Convert to a TVP
var params = new DynamicParameters();
parameters.Add("@TVP_UserInfo", dataTable.AsTableValuedParameter("[dbo].[InsertUserInfo]"));
using (var conn = new SqlConnection(ConfigurationManager.AppSettings["DatabaseConnection"]))
{
result = await conn.ExecuteAsync("InsertStoredProcedure", params, commanyType: CommandType.StoredProcedure);
}
}
public DataTable ToDataTable<T>(this HashSet<T> iHashSet)
{
DataTable dataTable = new DataTable();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor propertyDescriptor = props[i];
Type type = propertyDescriptor.PropertyType;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
type = Nullable.GetUnderlyingType(type);
dataTable.Columns.Add(propertyDescriptor.Name, type);
}
object[] values = new object[props.Count];
foreach (T iListItem in iHashSet)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(iListItem);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
insert 语句读取 TVP 并插入
CREATE PROCEDURE [InsertStoredProcedure]
(@TVP_UserInfo dbo.TVP_UserInfo READONLY)
AS
BEGIN
DECLARE @currentDate datetime = CURRENT_TIMESTAMP
INSERT INTO MyTable (UserName, Address,
AccountType, AccountNumber, AccountDisplay,
CreatedDate)
SELECT
UserName, Address,
AccountType, AccountNumber,
CASE
WHEN AccountNumber IS NULL
THEN ''
ELSE 'Anonymous'
END,
@currentDate
FROM
@TVP_UserInfo
END
这是 UDT 创建
CREATE TYPE [dbo].[TVP_UserInfo]
AS TABLE
(
UserName,
Address,
AccountType,
AccountNumber
)
我偶尔会得到重复,我不知道它们是如何或从哪里来的,因为每条消息都应该是唯一的,因为我使用的是哈希集。
我在想它是导致它的多线程但是,如果我只运行一个任务,有时我仍然会得到重复项。 如果您注意到创建的日期一直到毫秒都是完全相同的。 Id
(主键)不同,但剩余的行数据是实际重复的。
结果看起来像这样
ID | 用户名 | 地址 | 帐号 | 账户显示 | 创建日期 |
---|---|---|---|---|---|
1 | 乔 | 乔斯地址1 | 123456 | 匿名的 | 2022-08-01 01:45:52:352 |
1 | 乔 | 乔斯地址1 | 123456 | 匿名的 | 2022-08-01 01:45:52:352 |
UserName 是否允许在您的数据库中有重复项? 如果它不能包含重复项,我建议在该列上添加一个唯一索引(至少在开发中)。 这可能有助于您捕获导致重复的代码。
我可以看到一些事情:首先,您需要等待所有任务,而不仅仅是最后一个任务。
var tasks = new List<Task>
foreach(job in jobList)
{
tasks.add(Task.Run(() => ProcessJobs(job)));
}
Task.WaitAll(tasks.ToArray());
其次,我看不到 ProcessJobs 代码块将如何工作。
但是我认为您遇到的问题是代码将有多个线程访问getMessageFromQueue。 那么 is 和它的依赖是可重入和线程安全的吗? 如果所有的工作都是同步的,你可以使用锁 object 来限制它,如果你有其他异步工作最好使用 SemaphoreSlim 而不是锁,但锁会给你这个想法。
锁的例子
private lockobj = new lockobj();
private async Task<string> ProcessJobs(Job job)
{
lock (lockobj)
{
var messageData = getMessageFromQueue(message);
}
/// rest of your code .... and return value
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.