/// <summary> /// Constructor. /// </summary> /// <param name="transBase">The parent <see cref="BaseTransaction" />.</param> /// <param name="position">The <see cref="ILogPosition" /> for the first <see cref="IOperation" /> for this transaction.</param> internal Transaction(BaseTransaction transBase, ILogPosition position) { this.syncLock = transBase.Manager.SyncRoot; this.transBase = transBase; this.position = position; this.isOpen = true; this.resourceData = null; }
/// <summary> /// Reads the operation from the specified position in the log. /// </summary> /// <param name="resource">The parent <see cref="ITransactedResource" /> responsible for deserializing the operation.</param> /// <param name="position">See the <see cref="ILogPosition" />.</param> /// <returns>The <see cref="IOperation" /> read from the log.</returns> /// <exception cref="TransactionException">Thrown if the log is not open.</exception> public IOperation Read(ITransactedResource resource, ILogPosition position) { using (TimedLock.Lock(syncLock)) { if (!isOpen) { throw new TransactionException(NotOpenMsg); } return(operations[((MemoryLogPosition)position).Index]); } }
/// <summary> /// Truncates the log to the position passed. /// </summary> /// <param name="position">The <see cref="ILogPosition" /> defining where the truncation should occur.</param> /// <remarks> /// <note> /// This property can only be called if the operation log is in <see cref="OperationLogMode.Undo" /> /// mode. /// </note> /// <para> /// This method is used in combination with the <see cref="Position" /> property to roll /// back operations within a base transaction. /// </para> /// </remarks> /// <exception cref="TransactionException">Thrown if the log isn't open or if the mode isn't <see cref="OperationLogMode.Undo" />.</exception> public void Truncate(ILogPosition position) { using (TimedLock.Lock(this)) { if (file == null) { throw new TransactionException(ClosedMsg); } if (mode != OperationLogMode.Undo) { throw new TransactionException("Write is available only when the log is in UNDO mode."); } file.SetLength(((FileLogPosition)position).Position); } }
/// <summary> /// Returns the set of <see cref="ILogPosition" /> for each operation from the end of the /// log to the <paramref name="position" /> passed, in the reverse order that the operations /// were added to the log. /// </summary> /// <param name="position">The limit position.</param> /// <returns>The operation position list.</returns> /// <exception cref="ArgumentException">Thrown if the position passed is not valid.</exception> /// <exception cref="TransactionException">Thrown if the log is not open or is corrupt.</exception> public List <ILogPosition> GetPositionsTo(ILogPosition position) { var list = new List <ILogPosition>(); var pos = (FileLogPosition)position; long length; int cb; using (TimedLock.Lock(this)) { if (file == null) { throw new TransactionException(ClosedMsg); } if (pos.Position < HeaderSize || pos.Position > file.Length) { throw new ArgumentException("Invalid log position.", "position"); } length = file.Length; file.Position = pos.Position; while (!file.Eof) { list.Add(new FileLogPosition(file.Position)); if (file.ReadInt32() != Magic) { throw new TransactionException(CorruptMsg); } cb = file.ReadInt32(); if (cb < 0 || cb + file.Position > length) { throw new TransactionException(CorruptMsg); } file.Position += cb; } } list.Reverse(); return(list); }
/// <summary> /// Returns the set of <see cref="ILogPosition" />s for each operation from the end of the /// log to the <paramref name="position" /> passed, in the reverse order that the operations /// were added to the log. /// </summary> /// <param name="position">The limit position.</param> /// <returns>The operation position list.</returns> /// <exception cref="TransactionException">Thrown if the log is not open.</exception> public List <ILogPosition> GetPositionsTo(ILogPosition position) { int index = ((MemoryLogPosition)position).Index; List <ILogPosition> positions; using (TimedLock.Lock(syncLock)) { if (!isOpen) { throw new TransactionException(NotOpenMsg); } positions = new List <ILogPosition>(operations.Count - index); for (int i = operations.Count - 1; i >= index; i--) { positions.Add(new MemoryLogPosition(i)); } return(positions); } }
/// <summary> /// Reads the operation from the specified position in the log. /// </summary> /// <param name="resource">The parent <see cref="ITransactedResource" /> responsible for deserializing the operation.</param> /// <param name="position">See the <see cref="ILogPosition" />.</param> /// <returns>The <see cref="IOperation" /> read from the log.</returns> public IOperation Read(ITransactedResource resource, ILogPosition position) { int cb; long recEndPos; string description; IOperation operation; using (TimedLock.Lock(this)) { if (file == null) { throw new TransactionException(ClosedMsg); } file.Position = ((FileLogPosition)position).Position; if (file.ReadInt32() != Magic) { throw new TransactionException(CorruptMsg); } cb = file.ReadInt32(); if (cb <= 0 || cb + file.Position > file.Length) { throw new TransactionException(CorruptMsg); } recEndPos = file.Position + cb; description = file.ReadString32(); operation = resource.ReadOperation(file); if (file.Position != recEndPos) { SysLog.LogWarning("ITransactedResource.ReadOperation() returned with an unexpected stream position."); file.Position = recEndPos; } return(operation); } }
/// <summary> /// Truncates the log to the position passed. /// </summary> /// <param name="position">The <see cref="ILogPosition" /> defining where the truncation should occur.</param> /// <remarks> /// <note> /// This property can only be called if the operation log is in <see cref="OperationLogMode.Undo" /> /// mode. /// </note> /// <para> /// This method is used in combination with the <see cref="Position" /> property to roll /// back operations within a base transaction. /// </para> /// </remarks> /// <exception cref="TransactionException">Thrown if the log isn't open or if the mode isn't <see cref="OperationLogMode.Undo" />.</exception> public void Truncate(ILogPosition position) { using (TimedLock.Lock(syncLock)) { if (!isOpen) { throw new TransactionException(NotOpenMsg); } if (mode != OperationLogMode.Undo) { throw new TransactionException("Write is available only when the log is in UNDO mode."); } var pos = (MemoryLogPosition)position; if (pos.Index < 0 || pos.Index > operations.Count) { throw new TransactionException("Invalid operation log position [length={0} pos={1}].", operations.Count, pos.Index); } operations.RemoveRange(pos.Index, operations.Count - pos.Index); } }