我是否正确地使用了这个交易?

本文关键字:交易 是否 正确地 | 更新日期: 2023-09-27 18:15:04

我阅读了VehicleMovementEvent对象的列表,其中大多数是停车场区域的普通入口和出口。这些都有入口或出口的指示,以及日期和时间。我使用这个列表来生成一个VehiclePresence对象列表,它告诉我从开始时间到结束时间,从两个匹配的VehicleMovementEvent对象中收集到车辆出现在x区域。我只想处理整个列表,或者什么都不处理,所以交易似乎是合适的。

我不经常在代码中使用事务,那么,我这样做对吗?特别是w.r.t.隔离级别等

var opts = new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead, Timeout = new TimeSpan(0, 0, 10, 0) };
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, opts))
{
    var vehicleMovements = startsbatch.Movements
                                .Where(m => m.MovementType.Direction == VehicleMovementEventType.Entry)
                                .OrderBy(m => m.EmpNo)
                                .ThenBy(m => m.MovementDateTime);

    presenceBatch.StartDateTime = DateTime.Now;
    presenceBatch.MovementBatch = startsbatch;
    _dbContext.VehiclePresenceBatches.Add(presenceBatch);
    _procTrace.TraceInformation("New VehiclePresencesBatch created. Id: {0}.", presenceBatch.Id);
    foreach (var movement in vehicleMovements)
    {
        var presence = new VehiclePresence 
                            {
                                PresenceBatch = presenceBatch,
                                EmpNo = movement.EmpNo,
                                Location = movement.Location,
                                StartDateTime = movement.MovementDateTime,
                                StartMovementBatchId = movement.BatchId,
                                StartMovementLineId = movement.LineId,
                                StartMovementId = movement.Id
                            };
        _dbContext.VehiclePresences.Add(presence);
        returnList.Add(presence);
    }
    _dbContext.SaveChanges();
    scope.Complete();
    _procTrace.TraceInformation("{0} VehicleMovements processed. {1} VehiclePresences created", vehicleMovements.Count(), returnList.Count);
}

我是否正确地使用了这个交易?

是否创建startsbatch变量并将其插入数据库中,作为该方法中添加的VehiclePresenceBatch的一部分?因为如果是这样,那么您根本不需要启动自己的事务,因为EntityFramework的DBContext.SaveChanges()方法启动了自己的事务(参见此)。如果您不使用EF,那么您只需要一个事务包装对SaveChanges的调用,使用ReadCommitted作为隔离级别。

如果startsBatch中的信息已经存在于数据库中,但您不关心其他用户在您阅读后更新它,那么您的情况与上述相同,EF将为您处理事务。

只有当startsBatch已经存在并且你担心其他用户进程在你读取它之后更新该数据时,你才需要更多的注意:

  • 一种选择是加入一些乐观并发检查位置,例如在保存记录和提高时比较时间戳如果时间戳与您最初读取的时间戳不同,则会产生错误。在这种情况下,EF使用的事务仍然是可以的。(如果并发性检查是由EF或您自己的存储过程完成的)

  • 另一个选择是包含您的代码,包括读取startsBatch的代码段并使用Repeatable ReadSerializable隔离级别。你可以想象这使得这个系统不太可扩展,因为它会阻止其他人试图修改/更新那些事务期间的行。(可序列化的更多限制性的,甚至阻止新行插入)看看这个问题

根据经验,在使用SerializableRepeatable Read隔离级别时应该非常小心。在大多数情况下,使用像Read Commited这样限制较少的隔离级别,加上一些乐观的并发检查(如果需要的话,通常在更新操作中)应该足够了,并且会执行得更好。

我还想提到,如果您仍然需要事务,请考虑使用TransactionScopeOption.Required。这将只在没有环境事务的情况下启动一个新事务。因此,如果你的方法是作为另一个事务的一部分调用的,它将是该事务的一部分。