【问题标题】:Transaction accept null, and don't rollback事务接受null,并且不回滚
【发布时间】:2016-08-31 17:50:03
【问题描述】:

我会尽量做到具体一点。

我正在将 CSV 文件读入DataGridView,如果用户验证信息,用户可以看到该信息,他发送到 SQL Server 数据库。

我正在使用带有参数的存储过程事务来发送信息。

从 Visual Studio 调用:

using (SqlConnection conn = new SqlConnection(CadenaConexionBD.cs)) {
    conn.Open();

    for (int i = 0; i < dgvTrabajadores.Rows.Count - 1; i++){
        using (SqlCommand cmd = new SqlCommand("SistemaFAIFAP.cargaTransTrabajadores", conn)){
            cmd.CommandType = CommandType.StoredProcedure;
            guid = Guid.NewGuid();
            string stringGuid = guid.ToString();

            cmd.Parameters.AddWithValue("@id", stringGuid);
            cmd.Parameters.AddWithValue("@ap", dgvTrabajadores.Rows[i].Cells["APELLIDOPATERNO"].Value.ToString());
            cmd.Parameters.AddWithValue("@am", dgvTrabajadores.Rows[i].Cells["APELLIDOMATERNO"].Value.ToString());
            cmd.Parameters.AddWithValue("@n", dgvTrabajadores.Rows[i].Cells["NOMBRE"].Value.ToString());
            cmd.Parameters.AddWithValue("@fn", DateTime.Parse(dgvTrabajadores.Rows[i].Cells["FECHANACIMIENTO"].Value.ToString()));
            cmd.Parameters.AddWithValue("@pn", dgvTrabajadores.Rows[i].Cells["PAISNACIMIENTO"].Value.ToString());
            cmd.Parameters.AddWithValue("@en", dgvTrabajadores.Rows[i].Cells["ENTIDADNACIMIENTO"].Value.ToString());
            cmd.Parameters.AddWithValue("@nn", dgvTrabajadores.Rows[i].Cells["NombreNacionalidad"].Value.ToString());
            cmd.Parameters.AddWithValue("@nss", dgvTrabajadores.Rows[i].Cells["NSS"].Value.ToString());
            cmd.Parameters.AddWithValue("@rfc", dgvTrabajadores.Rows[i].Cells["RFC"].Value.ToString());
            cmd.Parameters.AddWithValue("@hom", dgvTrabajadores.Rows[i].Cells["HOMONIMIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@curp", dgvTrabajadores.Rows[i].Cells["CURP"].Value.ToString());
            cmd.Parameters.AddWithValue("@ec", dgvTrabajadores.Rows[i].Cells["ESTADOCIVIL"].Value.ToString());
            cmd.Parameters.AddWithValue("@sex", dgvTrabajadores.Rows[i].Cells["SEXO"].Value.ToString());
            cmd.Parameters.AddWithValue("@ce", dgvTrabajadores.Rows[i].Cells["CORREOELECTRONICO"].Value.ToString());
            cmd.Parameters.AddWithValue("@cat", dgvTrabajadores.Rows[i].Cells["CATEGORIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@dc", dgvTrabajadores.Rows[i].Cells["DESCRIPCIONCATEGORIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@calle", dgvTrabajadores.Rows[i].Cells["CALLE"].Value.ToString());
            cmd.Parameters.AddWithValue("@num", dgvTrabajadores.Rows[i].Cells["NUMEROEXTERIOR"].Value.ToString());
            cmd.Parameters.AddWithValue("@col", dgvTrabajadores.Rows[i].Cells["COLONIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@mun", dgvTrabajadores.Rows[i].Cells["MUNICIPIO"].Value.ToString());
            cmd.Parameters.AddWithValue("@ent", dgvTrabajadores.Rows[i].Cells["ENTIDAD"].Value.ToString());
            cmd.Parameters.AddWithValue("@tel", dgvTrabajadores.Rows[i].Cells["TELEFONO"].Value.ToString());
            cmd.Parameters.AddWithValue("@dep", dgvTrabajadores.Rows[i].Cells["DEPENDENCIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@subdep", dgvTrabajadores.Rows[i].Cells["SUBDEPENDENCIA"].Value.ToString());
            cmd.Parameters.AddWithValue("@dir", dgvTrabajadores.Rows[i].Cells["DIRECCION"].Value.ToString());
            cmd.Parameters.AddWithValue("@depa", dgvTrabajadores.Rows[i].Cells["DEPARTAMENTO"].Value.ToString());
            cmd.Parameters.AddWithValue("@ofi", dgvTrabajadores.Rows[i].Cells["OFICINA"].Value.ToString());
            cmd.Parameters.AddWithValue("@npl", dgvTrabajadores.Rows[i].Cells["NUMEROPLAZA"].Value.ToString());
            cmd.Parameters.AddWithValue("@cvep", dgvTrabajadores.Rows[i].Cells["CLAVEPROGRAMATICA"].Value.ToString());
            cmd.Parameters.AddWithValue("@fig", DateTime.Parse(dgvTrabajadores.Rows[i].Cells["FECHAINGRESOGOBIERNO"].Value.ToString()));
            cmd.Parameters.AddWithValue("@nc", dgvTrabajadores.Rows[i].Cells["NUMEROCONTROL"].Value.ToString());
            cmd.Parameters.AddWithValue("@cvee", dgvTrabajadores.Rows[i].Cells["CLAVEEMPLEADO"].Value.ToString());
            cmd.Parameters.AddWithValue("@ior", dgvTrabajadores.Rows[i].Cells["IDOFICINARECAUDADORA"].Value.ToString());
            cmd.Parameters.AddWithValue("@fum", DateTime.Parse(dgvTrabajadores.Rows[i].Cells["FECULTMOV"].Value.ToString()));

            filasAfectadas = cmd.ExecuteNonQuery();
        }
    }
    if (filasAfectadas <= 0){
        MessageBox.Show("Los datos no se cargaron de manera correcta. \nContacte al Administrador del Sistema.", "Error al cargar la base de datos", MessageBoxButtons.OK, MessageBoxIcon.Error);
        conn.Close();
    }else{
        MessageBox.Show("Se cargó correctamente la información", "Datos agregados", MessageBoxButtons.OK, MessageBoxIcon.Information);
        validacion = 1;
        conn.Close();
    }
}

我在数据库中的存储:

DevCod 解决方案第二版

ALTER PROCEDURE [SistemaFAIFAP].cargaTransTrabajadores 
@id uniqueidentifier, @ap NVARCHAR (64), @am NVARCHAR (64), @n NVARCHAR (64) = NULL,
@fn DATE, @pn NVARCHAR (50), @en NVARCHAR (50), @nn NVARCHAR (50),
 @nss NVARCHAR (11), @rfc NVARCHAR (10), @hom NVARCHAR (3), @curp NVARCHAR (18),
 @ec NVARCHAR (50), @sex NVARCHAR (50), @ce NVARCHAR (50), @cat NVARCHAR (6),
 @dc NVARCHAR (64), @calle NVARCHAR (255), @num NVARCHAR (10), @col NVARCHAR (60),
 @mun NVARCHAR (50), @ent NVARCHAR (50), @tel NVARCHAR (10), @dep NVARCHAR (3),
 @subdep NVARCHAR (2), @dir NVARCHAR (3), @depa NVARCHAR (3), @ofi NVARCHAR (4),
 @npl NVARCHAR (4), @cvep NVARCHAR (6), @fig DATE, @nc NVARCHAR (64), @cvee NVARCHAR (64),
 @ior FLOAT, @fum 
DATE AS
BEGIN TRY
BEGIN TRANSACTION 
IF(ISNULL(@n,'')='' OR @n = NULL OR @n = '')
BEGIN
RAISERROR ('@n is null or empty. This is not allowed. Insert will be    rollbacked',-- Message text to return to the UI.
       16, -- Severity.
       1 -- State
       )
END
INSERT INTO sistemaFAIFAP.TrabajadorBuffer
VALUES(
    @id,        @ap,        @am,        @n,     @fn,
    @pn,        @en,        @nn,        @nss,       @rfc,
    @hom,       @curp,      @ec,        @sex,       @ce,
    @cat,       @dc,        @calle,     @num,       @col,
    @mun,       @ent,       @tel,       @dep,       @subdep,
    @dir,       @depa,      @ofi,       @npl,       @cvep,
    @fig,       @nc,        @cvee,      @ior,       @fum
) 
COMMIT TRANSACTION
END TRY
BEGIN CATCH 
ROLLBACK TRANSACTION
SELECT  ERROR_NUMBER() AS ErrorNumber,ERROR_SEVERITY() AS ErrorSeverity,ERROR_STATE() AS ErrorState,ERROR_PROCEDURE() AS ErrorProcedure,ERROR_LINE() AS ErrorLine,ERROR_MESSAGE() AS ErrorMessage;
END CATCH

正如您在图片中看到的,我将null 字段作为测试。
我在字段Nombre 中将我的表定义为Not Null,但它没有显示错误消息,并且信息已插入到表中。

如何防止发送空记录?

(这是我第一次使用事务)为什么我的存储过程不显示错误消息并继续进行,好像字段Nombre 有值?

【问题讨论】:

  • 可能是从您的 C# 代码发送了一个空字符串。在您的存储过程中尝试在值中添加NULLIF(@n, '')
  • 好吧,这行得通,但现在该记录被省略了,即使是错误。我正在尝试发送 102 个注册表,但只加载 101 个。
  • As you can see in the pic, I put a null field as test - 不,我从图片中看不到。我可以从代码中看到您将dgvTrabajadores.Rows[i].Cells["NOMBRE"].Value.ToString() 传递给@n。那不会是null
  • 我不知道该说什么,猜猜……谢谢?你帮了很多忙!

标签: c# .net sql-server stored-procedures transactions


【解决方案1】:

您可以在 SQL 端验证参数以检查它们是否为空或空白。在这种情况下,您可以从过程和回滚事务中引发错误。您可以使用 RAISERROR 自定义错误消息,参考https://msdn.microsoft.com/en-us/library/ms178592.aspx

Create table Trabajador_TEST(n NVARCHAR (64) not null)
Go
CREATE PROCEDURE cargaTransTrabajadores_TEST
(@n NVARCHAR (64) = null)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION 
-- Place any number of Condition that you do not want to allow.
IF(ISNULL(@n,'')='')
Begin
RAISERROR ('@n is null or empty. This is not allowed. Insert will be rollbacked',-- Message text to return to the UI.
           16, -- Severity.
           1 -- State
           )
End

INSERT INTO Trabajador_TEST VALUES(@n) 
-- All Set, Save It.
COMMIT TRANSACTION
END TRY
BEGIN CATCH 

ROLLBACK TRANSACTION  -- Something Wrong so Do not Save It.

SELECT  ERROR_NUMBER() AS ErrorNumber,ERROR_SEVERITY() AS ErrorSeverity,ERROR_STATE() AS ErrorState,ERROR_PROCEDURE() AS ErrorProcedure,ERROR_LINE() AS ErrorLine,ERROR_MESSAGE() AS ErrorMessage; --Return the error message back to the UI.
END CATCH
END

执行 cargaTransTrabajadores_TEST null

此插入数据不应该工作。它应该将错误返回给 UI。

【讨论】:

  • 谢谢,很抱歉我的回答延迟了,我用你的想法尝试了新的东西,但我在 SqlException 中收到了一个新错误。我编辑我的 sql 代码以显示我做了什么。 ROLLBACK TRANSACTION请求没有对应的BEGIN TRANSACTION有解决办法吗?
  • 您在错误的位置得到了 RAISERROR。它应该在 BEGIN TRANSACTION 语句之后,因此当由于 RAISERROR 调用 BEGIN CATCH 时,它有一个要回滚的事务。顺便说一句:如果您处理 UI 代码,验证提示“'@N 的 UI 元素'为空,请指定一个值!”将避免在 SQL 端处理这些问题。
  • 实际上它运行并且不显示任何错误......只是省略了空白注册表。我更新了我的查询,并添加了一些“OR”,但是没有。
【解决方案2】:
  1. 首先,dgvTrabajadores.Rows.Count 不应包含无法保存的数据。使用 Javascript、.net 验证器将 UI 验证添加到网格。提示用户,“列不能为空”。
  2. 只为“for 循环”放置一个 try catch。在 catch 块中,将所有错误信息以字符串格式合并并显示在最后。

前;

string ErrorMsg = "";
for (int i = 0; i < dgvTrabajadores.Rows.Count - 1; i++){
try
{
  cmd.SavingOneRowAtOneTime()
}
catch(Exception ex)
{
  //Example, change it as you need.
  ErrorMsg += (ErrorMsg == "") ?  "Insert failed for Rows: " + i.ToString() : "," + i.ToString();
}
}
//At the end
if(ErrorMsg != "")
 MessageBox.Show(ErrorMsg);
}

如果这是您可以接受的答案,请将其标记为答案。谢谢!

【讨论】:

  • 1.听起来不错,从接口验证,如果某些列中包含空格,则不要让用户上传数据库。谢谢你的想法,我会实现这个,我会告诉你的。当我做出更改时,我会标记为答案。抱歉,我只有 7 个声望可以删除这个“-1”。
猜你喜欢
  • 2018-05-23
  • 2018-10-11
  • 2012-07-23
  • 1970-01-01
  • 2011-09-19
  • 1970-01-01
  • 2012-07-13
  • 2013-12-15
相关资源
最近更新 更多