2

我有一个我无法控制的 MS SQL 表,我需要写入。该表有一个不会自动递增的 int 主键。我不能使用存储过程,我想使用 Linq to SQL,因为它使其他处理变得非常容易。

我目前的解决方案是读取最后一个值,增加它,尝试使用它,如果我遇到冲突,再次增加它并重试。

沿着这些思路:

var newEntity = new Log()
{
    ID = dc.Logs.Max(l => l.ID) + 1,
    Note = "Test"       
};

dc.Logs.InsertOnSubmit(newEntity);

const int maxRetries = 10;
int retries = 0;

bool success = false;
while (!success && retries < maxRetries)
{               
    try
    {                                                           
        dc.SubmitChanges();
        success = true;
    }
    catch (SqlException)
    {
        retries++;
        newEntity.ID = dc.Logs.Max(l => l.ID);                  
    }
}           
if (retries >= maxRetries)
{
    throw new Exception("Bummer...");
}

有没有人有更好的解决方案?

编辑:感谢Jon,我简化了最大 ID 计算。我仍然处于 SQL 思维模式。

4

4 回答 4

2

这看起来是一种获得最大 ID 的昂贵方法。你是否已经尝试过

var maxId = dc.Logs.Max(s => s.ID);

? 也许由于某种原因它不起作用,但我真的希望它......

(不可否认,SQL Server 对此进行了适当的优化。)

除此之外,对我来说它看起来还可以(很臭,但必然如此) - 但我不是这方面的专家......

于 2008-12-22T08:56:02.233 回答
0

您没有说明您的应用程序是否是唯一插入表格的应用程序。如果是,那么我会在应用程序/webapp 启动后立即获取最大值,并在每次需要下一个 ID 时使用 Interlocked.Increment (或者如果可以排除可能的竞争条件,则简单添加)。

于 2008-12-22T09:19:13.467 回答
0

您可以使用TransactionScope类将整个操作放入事务中,如下所示:

using  (TransactionScope scope = new TransactionScope()){
   var maxId = dc.Logs.Max(s => s.ID); 
   var newEntity = new Log(){        
       ID = maxId,        
       Note = "Test"           
   };
   dc.Logs.InsertOnSubmit(newEntity);
   dc.SubmitChanges();
   scope.Complete();
}  

通过将最大 ID 的检索和新记录的插入放在同一个事务中,您应该能够完成插入,而不必以您的方式重试。

使用这种方法可能会遇到的一个问题是事务死锁,尤其是在表被大量使用的情况下。做测试看看你是否需要额外的错误处理。

PS 我在我的代码中包含了 Jon Skeet 的代码以获取最大 ID,因为我很确定它会正常工作。:)

于 2008-12-22T09:23:14.613 回答
0

使 id 字段自动递增并让服务器处理 id 生成。

否则会遇到liggett78所说的问题。没有什么能阻止另一个线程在读取和提交该线程的最大 id 之间读取相同的 id。

于 2010-01-02T11:39:54.923 回答