0

我有一个表'testTable':

[Id]    [Name]
 0     'something'
 1     'something'
 2     'something'
 3     'something'

Id 列不是身份,而是主键,所以我将值添加为

INSERT INTO testTable VALUES (2, 'something')

我需要有只接受[Name]列的 C# 方法,将此参数插入表中并返回[Id]列的值。

示例:我使用传递的字符串“test”调用方法。结果应该是:

1) 在表中插入行[Id]= 4 和[Name]= 'test'

2) 方法返回[Id]4

我已经实现了具有 SQL 查询的方法,例如:

  declare @Id int;
 set @Id = ISNULL((SELECT MAX(ID) FROM testTable) + 1, 0);

 insert into testTable OUTPUT @Id  values (@Id, @name);

对于单线程它工作正常。但是如果我调用这个方法,例如,在

Parallel.For(0, 10, <lambda with method>);

我发现异常

违反 PRIMARY KEY 约束“ID_PK”。无法在对象“testTable”中插入重复键

我想我可以使用 C#lock关键字,但如果可以只使用 SQL 查询那就太好了。谢谢。

4

1 回答 1

1

您应该创建一个标识列,但您不能始终控制数据库设计。

当我遇到这种情况时,我使用辅助方法

    /// <summary>
    /// Execute a non-query with a retry to handle collisions with non-identity keys
    /// </summary>
    /// <remarks>The command is retried while unique constraint or duplicate key errors occur
    /// <note type="caution">To be meaningful the command must try different values on each try
    /// e.g. INSERT INTO.. (Key) VALUES (SELECT MAX(Key)+1, ...</note></remarks>
    /// <param name="command">Command to execute</param>
    /// <param name="retries">Maximum number of retries</param>
    /// <returns>Number of rows affected. </returns>
    public static int ExecuteNonQueryWithRetry(this SqlCommand command, int retries) {
        for (int failCount = 0;;) {
            try {
                return command.ExecuteNonQuery();
            }
            catch (SqlException ex) {
                const int UniqueConstraintViolation = 2627;
                const int DuplicateKey = 2601;

                if (++failCount >= retries || 
                    (ex.Number != UniqueConstraintViolation &&
                     ex.Number != DuplicateKey))
                    throw;
            }
        }
    }

辅助方法是这样使用的:

var command = new SqlCommand();
command.CommandText = "INSERT INTO testTable ( " +
                      "   ID " +
                      "  ,... " +
                      " ) " +
                      " VALUES ( " +
                      "    (SELECT COALESCE(MAX(Id), 0) + 1 " + 
                      "     FROM testTable) " +
                      "   ,... " +
                      " )";
// assign parameter, connection etc

const int MaxRetries = 2;

if (command.ExecuteNonQueryWithRetry(MaxRetries) != 1)
    throw new Exception("Oops");
于 2013-11-11T13:57:45.090 回答