我的要求是处理多个成本文件,其中有数百万条记录。在处理和验证之后,我必须将这些记录添加到数据库中。
为了获得更好的性能,我正在使用" yield
"在foreach循环中并一次返回一条记录,处理该记录并立即将该记录添加到具有文件号的数据库中。在此文件读取过程中,如果遇到任何数据验证错误,我会抛出InvalidRecordException。
我的要求是删除与该文件相关的表中的所有记录。简而言之,即使一条记录无效,我也希望将该文件标记为无效文件,而不是将该文件的单条记录添加到数据库中。
任何人都可以帮助我,我如何在这里使用TransactionScope。
public class CostFiles
{
public IEnumerable<string> FinancialRecords
{
get
{
//logic to get list of DataRecords
foreach (var dataRecord in DataRecords)
{
//some processing... which can throw InvalidRecord exception
yield return dataRecord;
}
yield break;
}
}
}
public void ProcessFileRecords(CostFiles costFile, int ImportFileNumber)
{
Database db = new Database();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
foreach (var record in costFile.FinancialRecords)
{
db.Add(record, ImportFileNumber);
}
}
catch(InvalidRecordException ex)
{
//here i want to delete all the records from the table where import file number is same as input paramter ImportFileNumber
}
}
}
答案 0 :(得分:4)
事务范围的目的是创建“全部或全部”场景,因此要么整个事务提交,要么全部提交。看起来你已经有了正确的想法(至少在TransactionScope
方面。在你调用TransactionScope.Complete()
之前,范围实际上不会将记录提交到数据库。如果Complete()
是如果没有调用,则在离开事务范围时会丢弃记录。您可以轻松地执行以下操作:
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
bool errorsEncountered = false;
try
{
foreach (var record in costFile.FinancialRecords)
{
db.Add(record, ImportFileNumber);
}
}
catch(InvalidRecordException ex)
{
//here i want to delete all the records from the table where import file number is same as input paramter ImportFileNumber
errorsEncountered = true;
}
if (!errorsEncountered)
{
scope.Complete();
}
}
或者你可以让Add抛出异常并在事务范围之外处理它,因为异常将导致Complete()
不被调用,因此没有添加任何记录。当我们已经知道它不会做任何事情时,这种方法还有一个额外的优点就是停止处理其他记录。
try
{
using (var scope = new TransactionScope(TransactionScopeOptions.Required))
{
foreach(var record in costFile.FinancialRecords)
{
db.Add(record, ImportFileNumber);
}
// if an exception is thrown during db.Add(), then Complete is never called
scope.Complete()
}
catch(Exception ex)
{
// handle your exception here
}
}
编辑如果您不希望将事务提升为分布式事务(可能具有其他安全性/网络要求),请确保为事务中的每个数据库调用重用相同的SqlConnection对象范围。
using (var conn = new SqlConnection("myConnectionString"))
{
conn.Open();
using (var scope = new TransactionScope(...))
{
foreach(var foo in foos)
{
db.Add(foo, conn);
}
scope.Complete();
}
}