Created
April 20, 2010 03:29
-
-
Save hugoware/371990 to your computer and use it in GitHub Desktop.
Use lambdas to set a series of methods to perform and then rollback if an exception takes place
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
//Requires ImpersonationContext that can be found | |
//at http://gist.github.com/357819 | |
using Hugoware.Security; | |
namespace Hugoware { | |
/// <summary> | |
/// Creates a | |
/// </summary> | |
public class WorkSequence { | |
#region Constructors | |
/// <summary> | |
/// Creates a new WorkSequence | |
/// </summary> | |
public WorkSequence() | |
: this(null) { | |
} | |
/// <summary> | |
/// Creates a new WorkSequence with a default ImpersonationContext to use | |
/// </summary> | |
public WorkSequence(ImpersonationContext defaultContext) { | |
this._Work = new List<WorkItem>(); | |
this.DefaultContext = defaultContext; | |
this.IgnoreRollbackErrors = true; | |
} | |
#endregion | |
#region Properties | |
/// <summary> | |
/// The context to default when no context is assigned | |
/// </summary> | |
public ImpersonationContext DefaultContext { get; set; } | |
/// <summary> | |
/// Gets or sets if errors when rolling back should be ignored | |
/// </summary> | |
public bool IgnoreRollbackErrors { get; set; } | |
//holds a list of work items | |
public IEnumerable<WorkItem> Items { | |
get { return this._Work.AsEnumerable(); } | |
} | |
private List<WorkItem> _Work; | |
#endregion | |
#region Events | |
/// <summary> | |
/// Raised when a work item cannot complete successfully | |
/// </summary> | |
public event Action<WorkSequence, WorkItem, int> Error; | |
/// <summary> | |
/// Raised when the work has finished | |
/// </summary> | |
public event Action<WorkSequence> Complete; | |
#endregion | |
#region Public Methods | |
/// <summary> | |
/// Adds a new work item to the sequence | |
/// </summary> | |
public void Add(Action perform) { | |
this.Add(perform, null, null); | |
} | |
/// <summary> | |
/// Adds a new work item to the sequence | |
/// </summary> | |
public void Add(Action perform, Action rollback) { | |
this.Add(perform, rollback, null); | |
} | |
/// <summary> | |
/// Adds a new work item to the sequence | |
/// </summary> | |
public void Add(Action perform, ImpersonationContext context) { | |
this.Add(perform, null, context); | |
} | |
/// <summary> | |
/// Adds a new work item to the sequence | |
/// </summary> | |
public void Add(Action perform, Action rollback, ImpersonationContext context) { | |
WorkItem item = new WorkItem(perform, rollback, context ?? this.DefaultContext); | |
this._Work.Add(item); | |
} | |
/// <summary> | |
/// Performs the work and waits for the items to complete | |
/// </summary> | |
public void Perform() { | |
WorkItem[] items = this._Work.ToArray(); | |
//start trying to perform the work first | |
int index; | |
for (index = 0; index < items.Length; index++) { | |
//try and perform the work for this item | |
try { | |
items[index].Perform(); | |
} | |
//any exceptions should fail | |
catch { | |
break; | |
} | |
} | |
//if this failed to complete all the work, rollback | |
if (index < items.Length) { | |
if (this.Error is Action<WorkSequence, WorkItem, int>) { | |
this.Error(this, items[index], index); | |
} | |
//start rolling back and performing undo work in reverse | |
for (; index-- > 0; ) { | |
try { | |
items[index].Rollback(); | |
} | |
//check if we are ignoring errors | |
catch { | |
if (!this.IgnoreRollbackErrors) { | |
throw; | |
} | |
} | |
} | |
} | |
//finally, let everyone know this has finished | |
if(this.Complete is Action<WorkSequence>) { | |
this.Complete(this); | |
} | |
} | |
/// <summary> | |
/// Performs the work in the sequence | |
/// </summary> | |
public void PerformAsync(AsyncCallback callback) { | |
Action perform = this.Perform; | |
perform.BeginInvoke(callback, this); | |
} | |
#endregion | |
#region Helper Classes | |
//holds individual work items for a queue | |
public class WorkItem { | |
#region Constructors | |
//creates a work item that relies on an | |
//exception to know when it has failed | |
public WorkItem(Action perform, Action rollback, ImpersonationContext context) { | |
this._Context = context; | |
this._Perform = perform; | |
this._Rollback = rollback; | |
} | |
#endregion | |
#region Members | |
//contains the work to perform | |
Action _Perform; | |
Action _Rollback; | |
//contains an optional context to use | |
ImpersonationContext _Context; | |
#endregion | |
#region Public Methods | |
/// <summary> | |
/// Returns information about the work to perform | |
/// </summary> | |
public MethodInfo GetPerformMethod() { | |
if (this._Perform == null) { return null; } | |
return this._Perform.Method; | |
} | |
/// <summary> | |
/// Returns information about the rollback method to use | |
/// </summary> | |
public MethodInfo GetRollbackMethod() { | |
if (this._Rollback == null) { return null; } | |
return this._Rollback.Method; | |
} | |
//attempts to call the Perform method | |
public void Perform() { | |
this._Work(this._Perform); | |
} | |
// | |
public void Rollback() { | |
this._Work(this._Rollback); | |
} | |
#endregion | |
#region Private Methods | |
//performs the correct function | |
private void _Work(Action work) { | |
//make sure there was work to begin with | |
if (work == null) { return; } | |
//check for any credentials to impersonate | |
if (this._Context is ImpersonationContext) { | |
this._Context.Execute(work); | |
} | |
//just perform the work normally | |
else { | |
work(); | |
} | |
} | |
#endregion | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment