简体   繁体   English

我是否正确使用此交易?

[英]Am I using this transaction correctly?

I read a list of VehicleMovementEvent objects, most of which are plain entrances to and exits from a parkade zone. 我阅读了VehicleMovementEvent对象的列表,其中大多数是到停车场区域的普通入口和出口。 These have an indicator of entrance or exit, and a date and time. 它们具有入口或出口的指示符以及日期和时间。 I use this list to produce a list of VehiclePresence objects that tell me a vehicle was present in zone x from a start time until an end time, gleaned from two matching VehicleMovementEvent objects. 我使用此列表生成了VehiclePresence对象的列表,该对象告诉我从两个匹配的VehicleMovementEvent对象收集的车辆从开始时间到结束时间在区域x中存在。 I would just like the whole list to be processed, or nothing to be processed, so a transaction seems fitting. 我只希望处理整个列表,或者不处理任何内容,因此事务似乎很合适。

I don't often use transactions in code, so, am I doing this right? 我不经常在代码中使用事务,所以,我这样做对吗? Especially wrt the isolation level etc. 特别是隔离级别等。

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

Is the startsbatch variable being created and inserted in the database as part of the VehiclePresenceBatch being added in that method? startsbatch被创建并在数据库中作为的一部分插入的变量VehiclePresenceBatch在该方法中被添加? Because if so, then you don't need to start your own transaction at all as the EntityFramework's DBContext.SaveChanges() method starts its own transaction( see this ). 因为如果是这样,那么您根本就不需要启动自己的事务,因为EntityFramework的DBContext.SaveChanges()方法可以启动其自己的事务( 请参阅 DBContext.SaveChanges() )。 If your are not using EF, then you would just need a transaction wrapping the call to SaveChanges, using ReadCommitted as isolation level. 如果您没有使用EF,则只需使用ReadCommitted作为隔离级别的事务包装对SaveChanges的调用即可。

If the information in startsBatch already exists in the database but you don't care about other users updating it after you have read it, you are in the same situation as above and EF will take care of the transaction for you. 如果startsBatch的信息已经存在于数据库中,但是您不关心其他用户在阅读它之后对其进行更新,那么您将处于与上述相同的情况,并且EF将为您处理事务。

You need to pay more attention only if startsBatch already exists and you are worried about other users\\processes updating that data after you read it: 仅当startsBatch已经存在并且您担心其他用户\\进程在读取数据后更新该数据时,才需要更加注意:

  • One option would be to put some optimistic concurrency checks in place, like comparing a timestamp when saving the records and raising an error if the timestamp is not the same than the one you initially read. 一种选择是进行一些乐观的并发检查,例如在保存记录时比较时间戳,如果时间戳与最初读取的时间戳不同,则会引发错误。 In this case, the transaction used by EF is still ok. 在这种情况下,EF使用的事务仍然可以。 (Providing the concurrency check is done by EF or your own stored procedure) (提供并发检查是由EF还是您自己的存储过程完成的)

  • The other option is to include your code, including the piece of code that reads the startsBatch data inside a transaction and use a Repeatable Read or Serializable isolation levels. 另一个选择是包括您的代码,包括在事务中读取startsBatch数据并使用“可Repeatable Read或“可Serializable隔离级别的代码段。 As you could imagine this makes the system less scalable as it would block anyone else trying to modify\\update those rows for the duration of the transaction. 可以想象,这会使系统的可伸缩性降低,因为它将阻止其他尝试在事务期间修改/更新这些行的人。 (Serializable being more restrictive, even preventing from new rows to be inserted) Have a look at this question and the msdn here and here . (可序列化的限制更大,甚至阻止插入新行)在这里这里看看这个问题和msdn。

As a rule of thumb, you should be really careful when using Serializable and Repeatable Read isolation levels. 根据经验,在使用“可Serializable和“可Repeatable Read隔离级别时,您应该非常小心。 Using a less restrictive isolation level like Read Commited , with some optimistic concurrency check (if needed at all, usually in update operations) should be enough in most cases and will perform better. 在大多数情况下,使用具有较少限制的隔离级别(如Read Commited )和一些乐观的并发检查(如果需要,通常在更新操作中)就足够了,并且性能会更好。

I also wanted to mention that in case you still need a transaction, consider using TransactionScopeOption.Required . 我还想提到,如果您仍然需要事务,请考虑使用TransactionScopeOption.Required This will just start a new transaction only if there isn't already an ambient transaction. 仅当尚无环境事务时,这才开始新事务。 So if your method is called as part of another transaction, it would be part of that transaction. 因此,如果将您的方法作为另一个事务的一部分来调用,则它将作为该事务的一部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM