我们有一个存储简报订阅的表(ID,EmailAddress,MyNewsletter1等),当我们保存订阅时,我们首先检查是否已经为该电子邮件地址设置了订阅。如果有,我们更新该记录,如果没有,我们插入一个新记录。不知怎的,一个重复的电子邮件地址已经在那里潜入,我不知道如何。主键是ID,因此我们可以将其更改为EmailAddress,但我仍然很好奇这是如何发生的。它可能是一个并发问题吗?这是代码:
public static void SaveSubscription(NewsletterSubscription subscription)
{
using (MyDataContext db = new MyDataContext())
{
// does this email already have subscriptions?
NewsletterSubscription result = db.NewsletterSubscriptions.SingleOrDefault(r => r.Email == subscription.Email);
if (result != null)
{
// update instead of creating new record
result.MyNewsletter1 = subscription.MyNewsletter1;
result.MyNewsletter2 = subscription.MyNewsletter2;
result.MyNewsletter3 = subscription.MyNewsletter3;
result.MyNewsletter4 = subscription.MyNewsletter4;
}
else
{
// create new subscription record
subscription.RegisterDate = DateTime.Now;
db.NewsletterSubscriptions.InsertOnSubmit(subscription);
}
db.SubmitChanges();
}
}
谢谢,
Annelie
答案 0 :(得分:2)
听起来这只是进行读/插入的两个连接之间的竞争条件。一个修复可能是围绕这两个操作创建可序列化的事务:
using (var tran = new TransactionScope()) {
using (MyDataContext db = new MyDataContext()) {
// ... your existing code here
}
tran.Complete();
}
这会在select期间强制键范围锁定,因此执行读取的任何第二个线程都将被阻塞,直到事务完成为止;所以你不会让两个SPID看到“没有行”然后尝试插入;相反,第一个SPID将阻塞第二个,并在其完成工作的几个临界毫秒内;只有当第一个SPID决定是否(或不)插入数据(并调用Complete
或回滚)时,第二个SPID才会知道。
另外,请注意,您无需更改主键即可使其唯一 - 只需添加唯一约束即可。然后,您不必更改任何引用该表。
答案 1 :(得分:2)
正如一些人已经说过,这可能是一个并发问题,您应该让数据库帮助您避免它。如果您不想更改主键,则可以向电子邮件列添加唯一约束。
答案 2 :(得分:1)
代码似乎没问题,所以我猜一个并发问题。如果电子邮件应该只有一条记录,我建议在主键上添加/替换它。
在这种情况下,如果您尝试再次插入相同的记录,SubmitChanges将抛出异常。然后你可以在catch块里面测量一下来更新记录(如果合适的话就把它丢弃),然后继续执行。
通过提供正确的主键,获取数据库以帮助您避免重复。