Aspects for Data

Data access is so important and difficult that it seems every generation of software development technologies must invent its own solution. Modern data access frameworks typically address two issues: persistence of objects (object-relational mapping) and transaction handling.

In essence, object-relational mapping (ORM) frameworks attempt to enhance CLR objects with new behaviors: lazy loading of properties, dirty flag management, data binding, or validation. Being knowledgeable of PostSharp, you must have immediately recognized use cases for aspects. However, most of these frameworks use two poor man's alternatives of AOP: C# code generation (which makes sense when, but only when, code is generated from a model or from the database), or overriding of data objects using JIT-emitted code (which has a lot of limitations).

We're not arguing that you should create your own ORM solution (although it sometimes makes sense), but if you're attacking a larger project, it may pay off to consider using aspects to bind your plain old domain objects to the ORM framework of your choice.

In this section:

PostSharp Partners for Data

Don't reinvent the wheel. Our partners deliver state-of-the-art persistence data based on PostSharp. Check their offering.

DataObjects.NET 4.0

DataObjects.Net v4.0 combines comprehensive business logic layer development framework, object-relational mapper (ORM) and a set of persistent storage implementations enabling the same BLL code work everywhere by the same way dramatically reducing the resources you need to develop an application dealing with persistent data. read more

Persistence

Persistence frameworks typically need to address the following issues:

All the requirements above may be implemented using a LocationInterceptionAspect, which lets you replace the implementation of read and write operations by wathever you want.

Transaction Handling

Back in the old (good?) days of COM+, transaction handling was considered so important that it was implemented by the COM+ container; COM+ services (ServicedComponent in .NET) only had to specify their requirements for transaction handling declaratively (using TransactionAttribute in .NET).

Since then, Windows Component Services have been replaced by lighter technologies (web services, WCF), and the .NET Framework offers the namespace System.Transactions for lightweight transaction handling.

Yet, the .NET Framework still does not offer declarative transaction handling; it's not that the requirement has suddently disappeared, but rather that the .NET, as opposed to COM+, do not allow the platform to add behaviors to user code.

Thanks to aspect-oriented programming, we have the best of both worlds: declarative transaction handling (as well as monitoring and other aspects) from the COM+ world and lightweightness from .NET.

The following code shows you how to encapsulate the functionality of TransactionScope as a custom attribute:

/// <summary> /// Aspect that, when applied on a method, encloses it in a <see cref="TransactionScope"/>, /// and marks the <see cref="TransactionScope"/> as failed when the method results /// in an exception. /// </summary> [Serializable] public sealed class TransactionScopeAttribute : OnMethodBoundaryAspect { private readonly TransactionScopeOption option; public TransactionScopeAttribute() { } public TransactionScopeAttribute(TransactionScopeOption option) { this.option = option; } /// <summary> /// Method executed upon entry of the method to which this aspect is applied. /// </summary> /// <param name="args">Information about the method being executed.</param> public override void OnEntry(MethodExecutionArgs args) { args.MethodExecutionTag = new TransactionScope(this.option); } /// <summary> /// Method executed upon exit (success or failure) of the method to which /// this aspect is applied. /// </summary> /// <param name="args">Information about the method being executed.</param> public override void OnExit(MethodExecutionArgs args) { ((TransactionScope)args.MethodExecutionTag).Dispose(); } /// <summary> /// Method executed upon failure of the method to which this aspect is applied. /// </summary> /// <param name="args">Information about the method being executed.</param> public override void OnSuccess(MethodExecutionArgs args) { ((TransactionScope)args.MethodExecutionTag).Complete(); } }
Listing 1. A custom attribute that wraps target methods into a transaction scope.

This aspect ensures you will never forget to invoke the method TransactionScope.Complete again. More importantly, it makes the transactional behavior of methods visible so System.Reflection – for instance, you could generate documentation based on this aspect.

Summary

There are major benefits to using aspects inside your object persistence layer. The benefits are so important that you may want to design your ORM framework specifically for PostSharp, as X-tensive did with Data Objects .NET 4.0.