/// <summary> /// Adds an <see cref="ExceptionLogEntry"/>. If the <see cref="ExceptionLogEntry"/> cannot be created, the error is logged to the log file instead. /// </summary> /// <param name="operationName"></param> /// <param name="e"></param> public static void Log(string operationName, Exception e) { try { // log the error to the database using (PersistenceScope scope = new PersistenceScope(PersistenceContextType.Update, PersistenceScopeOption.RequiresNew)) { // disable change-set auditing for this context ((IUpdateContext)PersistenceScope.CurrentContext).ChangeSetRecorder = null; DefaultExceptionRecorder recorder = new DefaultExceptionRecorder(); ExceptionLogEntry logEntry = recorder.CreateLogEntry(operationName, e); PersistenceScope.CurrentContext.Lock(logEntry, DirtyState.New); scope.Complete(); } } catch (Exception x) { // if we fail to properly log the exception, there is nothing we can do about it // just log a message to the log file Platform.Log(LogLevel.Error, x); // also log the original exception to the log file, since it did not get logged to the DB Platform.Log(LogLevel.Error, e); } }
protected sealed override bool ClaimItem(TItem item) { try { using (var scope = new PersistenceScope(PersistenceContextType.Update)) { var context = (IUpdateContext)PersistenceScope.CurrentContext; context.ChangeSetRecorder.OperationName = this.GetType().FullName; // need to lock the item in context, to allow loading of extended properties collection by subclass context.Lock(item); // mark item as being in process MarkItemClaimed(item); // complete the transaction scope.Complete(); } return(true); } catch (EntityVersionException) { // if we get a version exception, the item has already been claimed by another process // this is not an error return(false); } }
protected sealed override IList <TItem> GetNextBatch(int batchSize) { using (var scope = new PersistenceScope(PersistenceContextType.Read)) { var items = GetNextEntityBatch(batchSize); scope.Complete(); return(items); } }
protected sealed override void ProcessItem(TItem item) { Exception error = null; using (var scope = new PersistenceScope(PersistenceContextType.Update)) { var context = (IUpdateContext)PersistenceScope.CurrentContext; context.ChangeSetRecorder.OperationName = this.GetType().FullName; // need to lock the item in context, to allow loading of extended properties collection by subclass context.Lock(item); try { // take action based on item ActOnItem(item); // ensure that the commit will ultimately succeed context.SynchState(); // success callback OnItemSucceeded(item); // complete the transaction scope.Complete(); } catch (Exception e) { // one of the actions failed Platform.Log(LogLevel.Error, e); error = e; } } // exceptions thrown upon exiting the using block above are intentionally not caught here, // allow them to be caught by the calling method // post-processing if (error == null) { AfterCommit(item); } else { UpdateItemOnError(item, error); } }
private void UpdateItemOnError(TItem item, Exception error) { // use a new scope to mark the item as failed, because we don't want to commit any changes made in the outer scope using (var scope = new PersistenceScope(PersistenceContextType.Update, PersistenceScopeOption.RequiresNew)) { var failContext = (IUpdateContext)PersistenceScope.CurrentContext; failContext.ChangeSetRecorder.OperationName = this.GetType().FullName; // bug #7191 : Reload the TItem in this scope; using the existing item results in NHibernate throwing a lazy loading exception var itemForThisScope = failContext.Load <TItem>(item.GetRef(), EntityLoadFlags.None); // lock item into this context failContext.Lock(itemForThisScope); // failure callback OnItemFailed(itemForThisScope, error); // complete the transaction scope.Complete(); } }
/// <summary> /// Disposes of this scope. /// </summary> /// <remarks> /// If this scope is associated with an update context and is the owner of that context, /// then disposal will commit or rollback the changes made in the update context, depending /// on whether this scope was marked Completed. If <see cref="Complete"/> was called on this scope, /// then disposal will attempt to commit the update context, otherwise it will simply dispose it, /// which is effectively a rollback. /// </remarks> public void Dispose() { if (!_disposed) { _disposed = true; if (this != _head) { throw new InvalidOperationException("Disposed out of order."); } try { if (OwnsContext) { CloseContext(); } else { // if the vote is still "undecided", treat it as an abort if (_vote == Vote.Undecided) { _vote = Vote.Abort; // we have an inherited context, so we need to propagate "aborts" up to the parent _parent._vote = Vote.Abort; } } } finally { // if CloseContext fails, we are still disposing of this scope, so we set the head // to point to the parent _head = _parent; } } }
/// <summary> /// Creates a new persistence scope for the specified context. The scope assumes ownership of the context /// and closes it when the scope terminates. /// </summary> /// <param name="context"></param> public PersistenceScope(IPersistenceContext context) { _context = context; _parent = _head; _head = this; }