0

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

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

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);
}
4

1 回答 1

1

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

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

startsBatch只有当已经存在并且您担心其他用户\进程在您阅读后更新该数据时,您才需要更加注意:

  • 一种选择是进行一些乐观并发检查,例如在保存记录时比较时间戳,如果时间戳与您最初读取的时间戳不同,则会引发错误。这种情况下,EF使用的事务还是可以的。(如果并发检查由 EF 或您自己的存储过程完成)

  • 另一种选择是包含您的代码,包括读取startsBatch 事务内数据并使用 aRepeatable ReadSerializable隔离级别的代码。正如您可以想象的那样,这会使系统的可扩展性降低,因为它会阻止其他任何人在事务期间尝试修改\更新这些行。(Serializable 更具限制性,甚至阻止插入新行)看看这个问题 和 msdn herehere

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

我还想提一下,如果您仍然需要交易,请考虑使用TransactionScopeOption.Required. 仅当还没有环境事务时,这才会开始一个新事务。因此,如果您的方法作为另一个事务的一部分被调用,它将成为该事务的一部分。

于 2013-05-22T16:55:29.807 回答