我读了一个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);
}
答案 0 :(得分:1)
是否正在创建startsbatch
变量并将其作为该方法中添加的VehiclePresenceBatch
的一部分插入数据库中?因为如果是这样,那么当EntityFramework的DBContext.SaveChanges()
方法启动自己的事务(see this)时,您根本不需要开始自己的事务。
如果您没有使用EF,那么您只需要使用ReadCommitted作为隔离级别将调用包装到SaveChanges。
如果startsBatch
中的信息已存在于数据库中,但您在阅读之后并不关心其他用户更新信息,那么您将处于与上述相同的情况,EF将处理该事务对你而言。
只有在startsBatch
已经存在并且您担心其他用户\进程在您阅读后更新该数据时,您需要更加注意:
一种选择是进行一些乐观的并发检查 地方,比如保存记录和提高时的时间戳 如果时间戳与您最初读取的时间戳不同,则会出错。在这种情况下,EF使用的事务仍然可以。 (提供并发检查由EF或您自己的存储过程完成)
另一个选项是包含您的代码,包括读取startsBatch
的代码段
事务中的数据并使用Repeatable Read
或
Serializable
隔离级别。你可以想象这会使系统成为现实
不太可扩展,因为它会阻止任何其他人试图修改\更新那些
交易持续时间的行。 (可序列化更多
限制性,甚至阻止插入新行)查看this
question
和msdn here和here。
根据经验,使用Serializable
和Repeatable Read
隔离级别时应该非常小心。使用限制较少的隔离级别(如Read Commited
),通过一些乐观的并发检查(如果需要,通常在更新操作中)应该足够大多数情况下并且性能会更好。
我还想提一下,如果您仍需要交易,请考虑使用TransactionScopeOption.Required
。只有在没有环境事务时才会启动新事务。因此,如果您的方法作为另一个事务的一部分被调用,那么它将成为该事务的一部分。