我的代码中有像
SaveListOfObjects
这样的方法,我在 foreach
循环中执行,然后 insert
记录到 SQL Server。
当我插入的数据没有错误时,它效果很好。但如果发生错误,则仅将有效数据插入 SQL 中。
我想做以下事情:
所以,我已经尝试过使用
TransactionScope
和 SqlTransaction
类,甚至使用 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;
}
在我看来,您正在循环并调用每个对象的 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>
}
您的问题在于您为数据库中写入的每个对象打开和关闭事务。当然,这意味着当您的数据出现问题时,之前正确的数据已经写入数据库并提交。所以无法回滚。
解决方案是在数据插入方法外部打开连接和事务,并在方法内部传递这些对象实例。
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();
}
这样,插入数据的方法在失败时,将引发由打开连接和事务的调用代码捕获的异常。然后,此调用代码可以调用回滚方法来撤消到目前为止插入的每个对象。
对数据输入对象进行约束更容易,例如限制某个文本框仅接受数字或您想要的任何内容。 作为另一个想法..在向数据库查询数据之前尝试检查数据准确性
一种方法是通过将列表中的每个对象映射到数据表的一行来从列表创建数据表。然后,您可以循环遍历 DataTable 行以执行插入操作,并将此代码块包含在事务中。这将允许您防止输入部分数据。
您可以参考此链接以了解如何将列表转换为数据表。