我正确使用此交易吗?

时间:2013-05-22 15:17:15

标签: c# .net transactions

我读了一个VehicleMovementEvent个对象的列表,其中大多数是从parkade区域进出的简单入口。它们有入口或出口的指示器,以及日期和时间。我使用此列表生成一个VehiclePresence对象列表,告诉我从开始时间到结束时间的区域x中存在车辆,从两个匹配的VehicleMovementEvent对象中收集。我只想处理整个列表,或者没有任何待处理的内容,因此交易似乎很合适。

我不经常在代码中使用事务,所以,我这样做了吗?特别是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);
}

1 个答案:

答案 0 :(得分:1)

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

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

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

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

  • 另一个选项是包含您的代码,包括读取startsBatch的代码段 事务中的数据并使用Repeatable ReadSerializable隔离级别。你可以想象这会使系统成为现实 不太可扩展,因为它会阻止任何其他人试图修改\更新那些 交易持续时间的行。 (可序列化更多 限制性,甚至阻止插入新行)查看this question 和msdn herehere

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

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