views:

95

answers:

3

I am trying to allow developers to extend my code at certain points of execution.

My specific example is a database transaction wrapper. The wrapper takes care of many details that we wanted to abstract away from the developer and is used on several projects.

Each project however has certain things they would like to do automatically during the transaction. I would like to add interception points that each project can setup to run code.

For example, each table in our database has an Entered Date field that is updated every time the record changes. We however want all the dates to be the same for a transaction across however many records are touched (i.e. 4 records table A, 1 record in table B, ...).

My thought is to define interception points "TransactionStarting", "TransactionStarted", "StatementExecuting", "StatementExecuted", ... and pass a context object to each point.

Then the project can define a class "EnteredDateManager" that stores the current date during the "TransactionStarted" point, and updates each object's EnteredDate property during the "StatementExecuting" point.

I would like to set this up in the web/app.config file and allow multiple interception classes to be registered. If more than one class is registered, they should be fired in the order they were registered.

I was thinking of just raising events, but I want order to matter. I also want to be able to share state between the different points. In my example above, the EnteredDate Property is set in the TransactionStarted point and used in the StatementExecuting point.

Is this the Chain of Responsibility pattern? AOP? It seems close to how the ASP.Net pipeline works, but they use events and don't guarantee ordering as far as I know.

Any direction/examples would be great.

Thanks

A: 

One way to do this is to just use the basic Strategy pattern. With Strategy, you're basically pushing functionality into a separate class which will be called by your class, rather than implementing the logic directly in the original class. The strategy classes can be included in the original class by setting them via interface-based properties, or through constructor arguments (or both). This way, the user can choose to inject different types of functionality into some processing flow defined by the original class.

Andy White
A: 

If this is .NET specific (you mentioned ASP.NET), I would highly recommend looking into the System.Transactions namespace, and read about creating and enrolling resource managers in a Transaction.

Using TransactionScope, you can create a transaction in context, and resource manager executing within that context can detect the presence of a transaction and enroll in it (this would be synonymous with your TransactionStart event). Once enrolled, each resource manager then has the option to either commit its changes, or back out and initiate a rollback of the transaction.

The Systen.Transactions namespace, which was introduced with .NET 2.0, offers some pretty powerful tools to create transactions and manage transacted resources. You have the option of both lightweight transactions, or if your needs are more complex, fully distributed transactions managed by the MSDTC service. Transactions can be single-phase commit, or two-phase commit, offering a lot of flexibility and stability in the face of transaction failure.

jrista
+1  A: 

Sounds like Aspect-Oriented Programming to me. Check out PostSharp.

Here's an example of tracing from their website:

public class TraceAttribute : OnMethodBoundaryAspect 
{ 
  public override void OnEntry( MethodExecutionEventArgs eventArgs) 
  { Trace.TraceInformation("Entering {0}.", eventArgs.Method);  } 

  public override void OnExit( MethodExecutionEventArgs eventArgs) 
  { Trace.TraceInformation("Leaving {0}.", eventArgs.Method);   } 
}

I use it to do logging/tracing, caching and performance monitoring.

Chris McCall