从.NET Forms Application调用SQL存储过程

时间:2018-04-06 06:55:42

标签: c# sql

从.NET Forms应用程序调用以下存储过程并且通常运行良好但我们曾经有过一些输出参数返回值的情况但稍后当我们尝试更新行时,该行不存在

这几乎就像交易正在回滚,但我们仍然收到了ID。有什么建议 ?这是随机发生的,直到现在我们还无法确定它发生的原因。

    ALTER PROCEDURE [dbo].[spAdm_QueueSummaryAdd]
    @fknProductId           INT,
    @fknProductRunId        INT,
    @nMailDelay             INT,
    @sProductRunMailServer  VARCHAR(20),
    @sFileName              VARCHAR(100),
    @sStatusCode            VARCHAR(10),
    @nQueueSummaryId        int OUTPUT,     
    @sStatus                NVARCHAR(MAX) OUTPUT    

AS

BEGIN TRANSACTION

    --check if exists
    IF EXISTS
        (SELECT
            pknQueueSummaryId
         FROM
            QueueSummary
         WHERE
            fknProductId = @fknProductId AND
            fknProductRunId = @fknProductRunId)

    --already exists
    BEGIN
        SET @nQueueSummaryId = 0
        SET @sStatus = 'pknQueueSummaryId already exists'
    END

    ELSE
    BEGIN

        --Add
        INSERT INTO [QueueSummary]
        (
            fknProductId,
            fknProductRunId,
            dtDateCreated,
            nMailDelay,
            sProductRunMailServer,
            sFileName,
            nTransactions,
            sStatusCode,
            dtDateModified,
            sModUser
        )
        VALUES
        (
            @fknProductId,
            @fknProductRunId,
            GETDATE(),
            @nMailDelay,
            @sProductRunMailServer,
            @sFilename,
            0,
            @sStatusCode,
            GETDATE(),
            'Superuser'
        )

        SET @nQueueSummaryId = SCOPE_IDENTITY()
        SET @sStatus = 'Success'
    END

--error control
IF @@ERROR <> 0
BEGIN
    SET @nQueueSummaryId = 0
    SET @sStatus = ERROR_MESSAGE()
    ROLLBACK TRANSACTION
    RETURN
END

--commit transaction
COMMIT TRANSACTION

为清楚起见,这里是表格定义

CREATE TABLE [dbo].[QueueSummary](
    [pknQueueSummaryId] [int] IDENTITY(1,1) NOT NULL,
    [fknProductRunId] [int] NOT NULL,
    [fknProductId] [int] NOT NULL,
    [nMailDelay] [int] NOT NULL,
    [sProductRunMailServer] [varchar](20) NOT NULL,
    [sFileName] [varchar](255) NOT NULL,
    [nTransactions] [int] NOT NULL,
    [sStatusCode] [varchar](10) NOT NULL,
    [dtDateCreated] [datetime] NOT NULL,
    [dtDateModified] [datetime] NOT NULL,
    [sModUser] [nchar](10) NOT NULL,
 CONSTRAINT [PK_QueueSummary] PRIMARY KEY CLUSTERED 
(
    [pknQueueSummaryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO

这是调用存储过程的C#代码

 public int Add(int fknProductId,
                          int fknProductRunId,
                          int nMailDelay,
                          string sProductRunMailServer,
                          string sFileName,
                          string sStatusCode)

        {
            //declare
            SqlCommand Command = null;
            SqlConnection Connection = null;


            try
            {
                //init
                Connection = new SqlConnection(_store.ConnectionString);
                Command = new SqlCommand("spAdm_QueueSummaryAdd", Connection);
                Command.CommandType = CommandType.StoredProcedure;
                Command.CommandTimeout = _store.nCommandTimeout;

                //add parameters
                SqlParameter par_fknProductId = new SqlParameter("@fknProductId", SqlDbType.Int);
                par_fknProductId.Direction = ParameterDirection.Input;
                par_fknProductId.Value = fknProductId;
                Command.Parameters.Add(par_fknProductId);

                //add parameters
                SqlParameter par_fknProductRunId = new SqlParameter("@fknProductRunId", SqlDbType.Int);
                par_fknProductRunId.Direction = ParameterDirection.Input;
                par_fknProductRunId.Value = fknProductRunId;
                Command.Parameters.Add(par_fknProductRunId);

                //add parameters
                SqlParameter par_nMailDelay = new SqlParameter("@nMailDelay", SqlDbType.Int);
                par_nMailDelay.Direction = ParameterDirection.Input;
                par_nMailDelay.Value = nMailDelay;
                Command.Parameters.Add(par_nMailDelay);

                //add parameters
                SqlParameter par_sProductRunMailServer = new SqlParameter("@sProductRunMailServer", SqlDbType.VarChar,20);
                par_sProductRunMailServer.Direction = ParameterDirection.Input;
                par_sProductRunMailServer.Value = sProductRunMailServer;
                Command.Parameters.Add(par_sProductRunMailServer);

                //add parameters
                SqlParameter par_sFileName = new SqlParameter("@sFileName", SqlDbType.VarChar, 100);
                par_sFileName.Direction = ParameterDirection.Input;
                par_sFileName.Value = sFileName;
                Command.Parameters.Add(par_sFileName);

                //add parameters
                SqlParameter par_sStatusCode = new SqlParameter("@sStatusCode", SqlDbType.VarChar, 10);
                par_sStatusCode.Direction = ParameterDirection.Input;
                par_sStatusCode.Value = sStatusCode;
                Command.Parameters.Add(par_sStatusCode);

                //add output parameter
                SqlParameter par_nQueueSummaryId = new SqlParameter("@nQueueSummaryId", SqlDbType.Int);
                par_nQueueSummaryId.Direction = ParameterDirection.Output;
                Command.Parameters.Add(par_nQueueSummaryId);

                SqlParameter par_sStatus = new SqlParameter("@sStatus", SqlDbType.NVarChar, -1);
                par_sStatus.Direction = ParameterDirection.Output;
                Command.Parameters.Add(par_sStatus);

                //execute
                Connection.Open();
                Command.ExecuteNonQuery();

                if(Command.Parameters["@sStatus"].Value.ToString() != "Success")
                {
                    _errorHandler.LogError("edd.Admin.QueueSummary Add() Status=" + Command.Parameters["@sStatus"].Value.ToString() + " |fknProductId=" + fknProductId + " |fknProductRunId=" + fknProductRunId + " |nMailDelay=" + nMailDelay + " |sProductRunMailServer=" + sProductRunMailServer + " |sFileName=" + sFileName +" |@nQueueSummaryId=" + Command.Parameters["@nQueueSummaryId"].Value  +  " |sStatusCode=" + sStatusCode);
                }

                return Convert.ToInt32(Command.Parameters["@nQueueSummaryId"].Value); 


            }
            catch (Exception errExp)
            {
                _errorHandler.LogError(errExp);
                return 0;
            }
            finally
            {
                //dispose objects
                if (Connection != null)
                {
                    if (Connection.State != ConnectionState.Closed)
                    {
                        Connection.Close();
                    }
                    Connection.Dispose();
                }
                if (Command != null)
                {
                    Command.Dispose();
                }
            }
        }

1 个答案:

答案 0 :(得分:1)

@@error返回最后一个语句的成功或失败,这不是INSERT语句(我认为你假设它是)。结合竞争条件,这是你的问题。允许SQL完成其工作(有利于简化代码),如下所示......

fknProductIdfknProductRunId列上添加唯一约束或唯一索引,而不是尝试自行检查。 (如果通过提供这两列来从此表中选择记录,请转到索引。)

删除所有事务位,因为其中唯一剩下的就是insert,它将具有隐式原子事务。

删除两个输出变量。状态可以从其他值派生。

插入后

Select Scope_Identity()。调用代码可以ExecuteScalar来获取身份值。

你应该留下一个存储过程,它只进行插入然后选择。

如果这对值已在使用中,则调用代码可以处理重复写入SqlException。