创建了重复记录,但为什么? (linq to sql)

时间:2011-02-25 12:12:07

标签: c# linq-to-sql

我们有一个存储简报订阅的表(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

3 个答案:

答案 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块里面测量一下来更新记录(如果合适的话就把它丢弃),然后继续执行。

通过提供正确的主键,获取数据库以帮助您避免重复。