如果插入失败一个记录删除所有以前插入的行,是否可以在事务中?

时间:2013-11-30 15:31:23

标签: c# sql-server insert rollback

我的代码中有SaveListOfObjects的方法,我在foreach循环内执行,然后insert记录到SQL Server。

当我插入的数据没有错误时,它很有用。但是如果发生错误,则只在SQL中插入有效数据。

我想做以下事情:

  1. 仅在整个数据有效且插入时发生一个错误的情况下插入所有记录。
  2. 删除SQL中以前保存的所有数据。
  3. 所以,我已经尝试了TransactionScopeSqlTransaction类,甚至尝试了SQL TRANSACTION,但我只能管理的是插入有效数据,省略了无效数据。

    现在,就我在网上搜索而言,我发现并行交易是不可能的。此外,SQL具有隔离级别,禁止并行任务。

    有没有办法在SQL中完成插入,如ALL或NOTHING?

    更新

    我的代码如下:

    public int Ramiz_SavePack(IPacking pack)
        {
            using (var conn = (SqlConnection)connector.GetConnection())
            {
                conn.Open();
                SqlTransaction transaction;
                var comm = (SqlCommand)connector.GetCommand("Ramiz_Pack_Save");
                comm.CommandType = CommandType.StoredProcedure;
                transaction = conn.BeginTransaction();
                comm.Transaction = transaction;
                int rowNum = 0;
    
    
                try
                {
                    if (!string.IsNullOrEmpty(pack.BrojKolete))
                        comm.Parameters.Add("@BrojKolete", SqlDbType.NVarChar).Value = pack.BrojKolete;
                    else
                        comm.Parameters.Add("@BrojKolete", SqlDbType.NVarChar).Value = DBNull.Value;
                    comm.Parameters.Add("@Bosanski", SqlDbType.NVarChar).Value = pack.Bosanski;
                    comm.Parameters.Add("@Kom", SqlDbType.Float).Value = pack.Kom;
                    comm.Parameters.Add("@Vrsta", SqlDbType.NVarChar).Value = pack.Vrsta;
                    comm.Parameters.Add("@Datum", SqlDbType.Date).Value = pack.Datum;
                    comm.Parameters.Add("@BrojKamiona", SqlDbType.Int).Value = pack.BrojKamiona;
    
                     rowNum = comm.ExecuteNonQuery();
                    transaction.Commit();
    
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    try
                    {
                        conn.Close();
                        transaction.Rollback();
                    }
                    catch (Exception ex2)
                    {
                        Console.WriteLine(ex2.Message);
                    }
    
                }
                return rowNum;
    
            }
        }
    

    并在此内部调用此方法:

     var pack = new Pack();
            for (int i = 1; i < lastRow; i++)
            {
                pack.Ramiz_SavePack(new Packing
                {
                    BrojKolete = Convert.ToString(brojKoleteRange.Offset[i, 0].Value2),
                    Bosanski = Convert.ToString(nazivArtiklaRange.Offset[i, 0].Value2),
                    Kom = Convert.ToDouble(komRange.Offset[i, 0].Value2),
                    Vrsta = Convert.ToString(vrstaRange.Offset[i, 0].Value2),
                    BrojKamiona = int.Parse(ddlBrojKamiona.SelectedItem.Value),
                    Datum = Convert.ToDateTime(txtDate.Text)
                });
                pnlMessageSuccess.Visible = true;
    
            }
    

4 个答案:

答案 0 :(得分:1)

在我看来,你正在循环并为每个对象调用save方法。如果事务存在于该循环周围,这不是问题,但事实并非如此。您将分别回滚/提交每个对象

您需要创建要保存的对象列表并将其发送到save方法,或者创建一个包装循环的事务,例如:

var list = new List<Pack>();

foreach(<your loop>)
{
   list.Add(new Pack(<some values>);
}

SavePacks(list);

void SavePacks(IList<Pack> items) 
{
    <create connection and transaction here and loop through inserting each item, rollback on error>
}

using(var tran = new SqlTransaction()) 
{
    <Do save logic here for all items and rollback if something went wrong>
}

答案 1 :(得分:1)

您的问题在于您为数据库中写入的每个对象打开和关闭事务。当然,这意味着当您遇到数据问题时,之前正确的数据已经写入数据库并已提交。所以它无法回滚。

解决方案是在数据插入方法之外打开连接和事务,并在方法中传递这些对象实例。

public int Ramiz_SavePack(IPacking pack, SqlConnection conn, SqlTransaction transaction)
{
    var comm = (SqlCommand)connector.GetCommand("Ramiz_Pack_Save");
    comm.Connection = conn;
    comm.CommandType = CommandType.StoredProcedure;
    comm.Transaction = transaction;
    ....
}


.....
try
{    
    using (var conn = (SqlConnection)connector.GetConnection())
    {
        conn.Open();
        SqlTransaction transaction = conn.BeginTransaction();
        var pack = new Pack();
        for (int i = 1; i < lastRow; i++)
        {
           pack.Ramiz_SavePack(new Packing
           {
               BrojKolete = Convert.ToString(brojKoleteRange.Offset[i, 0].Value2),
               Bosanski = Convert.ToString(nazivArtiklaRange.Offset[i, 0].Value2),
               Kom = Convert.ToDouble(komRange.Offset[i, 0].Value2),
               Vrsta = Convert.ToString(vrstaRange.Offset[i, 0].Value2),
               BrojKamiona = int.Parse(ddlBrojKamiona.SelectedItem.Value),
               Datum = Convert.ToDateTime(txtDate.Text)
           }, conn, transaction);
        }
        pnlMessageSuccess.Visible = true;
     }
   }
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    transaction.Rollback();
}

通过这种方式,插入数据失败的方法将引发由打开连接和事务的调用代码捕获的异常。然后,此调用代码可以调用rollback方法来撤消到目前为止插入的每个对象。

答案 2 :(得分:0)

对数据输入对象进行约束比较容易,例如约束某个文本框只接受数字或任何你想要的数字。 另外一个想法..尝试在查询数据之前检查数据准确性

答案 3 :(得分:0)

一种方法是通过将列表中的每个对象映射到DataTable的一行,从列表中创建DataTable。然后,您可以遍历DataTable行以执行插入操作,并将此代码块包含在Transaction中。这样可以防止输入部分数据。

您可以参考this link了解如何将列表添加到DataTable转换中。