SQL Server - 具有收益的 C# 事务范围

本文关键字:事务 范围 收益 Server SQL | 更新日期: 2023-09-27 17:55:44

我的要求是处理多个成本文件,其中包含数百万条记录。 处理和验证后,我必须将这些记录添加到数据库中。

为了获得更好的性能,我在foreach循环中使用" yield"并一次返回一条记录,处理该记录并立即将该记录添加到带有文件编号的数据库中。在此文件读取过程中,如果我遇到任何数据验证错误,我会抛出 InvalidRecordException。

我的要求是从与该文件相关的表中删除所有记录。 简而言之,即使一条记录无效 我想将该文件标记为无效文件,甚至不将该文件的一条记录添加到数据库中。

任何人都可以在这里帮助我,我如何在这里使用交易范围。

 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
                }
            }
        }

SQL Server - 具有收益的 C# 事务范围

事务范围的目的是创建一个"全有或全无"场景,因此要么整个事务提交,要么根本不提交。 看起来你已经有了正确的想法(至少在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();
    }
}