Archive

Download demo project source code

Yesterday we went over creating an aspect and applying it to a method. PostSharp is flexible in how aspects can be applied which results in outstanding benefits for us as developers. Imagine our exception wrapper aspect automatically being applied to any new method that is added to any class in the project. What about logging exceptions? No longer do we have to remember to add exception logging code to new methods. Of course, the practical applications are far beyond these simple scenarios but it gives you an idea of the power of PostSharp.

OnExceptionAspect is intended to work with methods. Yesterday we applied our aspect to the target method directly, which did the job but

  • What happens when we need to profile multiple methods?
  • What happens when we want to profile every method in every class?

That's a lot of work if we were to manually apply the aspect to each method.

OnMethodBoundaryAspect

PostSharp comes with another aspect that we can use called OnMethodBoundaryAspect. This class allows us to intercept certain points in a method’s execution by providing 4 virtual methods we can override

  • OnEntry - Before the execution of the method body
  • OnExit - Always called when a method is done executing even if there was an error
  • OnSuccess - Called only when a method is done executing and there were no exceptions
  • OnException - Called only when a method has stopped executing due to an unhandled exception

To give a clear understanding of multicasting, we’ll build a simple tracing aspect that will log the entry and exit of a method as it’s executed. Add a new file called MethodTraceAspect.cs and add the following code

[Serializable]
public class MethodTraceAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Debug.WriteLine(args.Method.Name + " started");
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Debug.WriteLine(args.Method.Name + " finished");
    }
}

This aspect is the, “Hello World” of Aspect Oriented Programming, but it serves our purpose for today. When applied to a method this aspect will write ‘Started’ and ‘Finished’ to the output window. Go ahead and apply it to the GetByName method in InMemoryDataStore.cs file

[DatabaseExceptionWrapper]
[MethodTraceAspect]
public IQueryable GetByName(string value)
{
    var res = _contactStore.Where(c => c.FirstName.Contains(value) 
                    || c.LastName.Contains(value));

    if (res.Count() < 1)
    {
        ThrowNoResultsException();
    }

    Thread.Sleep(3000);
    return res.AsQueryable();
}

Run the application and do a search. As expected you see

GetByName started
GetByName finished

In the output window.

Class Level Declaration

Instead of applying the aspect manually to every method, we can apply the aspect only once, on the class. PostSharp will automatically apply the aspect to all methods in the class. Let's try it. Remove the [MethodTraceAspect] from GetByName method and apply to the InMemoryDataStore class

[MethodTraceAspect]
internal class InMemoryDataStore : PostSharpDemo1.IContactRepository
{
    …
}

When we run the application there is a lot more being displayed in the output window, just by starting the application.

.cctor Started
InitializeData Started
set__contactStore Started
set__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
InitializeData Finished
.cctor Finished
.ctor Started
.ctor Finished
GetAll Started
get__contactStore Started
get__contactStore Finished
GetAll Finished

Notice that there are calls to methods not in the class. Remember that property getters and setters are turned into methods when your code is compiled which makes them eligible for receiving the aspect. The .cctor entry is the static constructor and .ctor is the instance constructor.

As interesting as this is, you won’t always want to have the aspect applied to absolutely everything. There are a few ways to avoid aspect application on a specific target. We'll cover those tomorrow.

Conclusion

Today we saw another aspect we can take advantage of and we also saw how we can use multicasting to apply an aspect to multiple targets with one line of code. Tomorrow we’ll look at even more multicasting.

 

self[5]Dustin Davis is an enterprise solutions developer and regularly speaks at user groups and code camps. He can be followed on twitter @PrgrmrsUnlmtd or his blog Programmers-Unlimited.com

CarlandRichard

For those of you who don’t yet subscribe to .NET Rocks! and its training video counterpart, dnrTV, you’re missing out on some wonderfully entertaining and informative talk shows.

Since 2002, Carl Franklin and Richard Campbell have been speaking with guests about programming in Microsoft .NET and, after nearly 650 episodes, show no signs of slowing down.

Gael was invited to be on .NET Rocks! in early February but, due to a bad phone line, he had to reschedule. Luckily both he and Carl attended this year’s NYC Code Camp, where they could finally sit down and record face-to-face. Or so he thought…

Listen to the first few minutes of the podcast to learn how Carl and Richard were finally able to record the show with Gael. Then, after you’ve had a good chuckle, continue listening to the podcast to learn more about programming with aspects.

DotNet Rocks

Listen to the podcast: http://www.dotnetrocks.com/default.aspx?showNum=640

Highlights include:

10:32 – What is an aspect?

12:10 – Using PostSharp for more than just Tracing and Logging

14:12 – Why having a bug in your aspect is safer than in the implementation of the manual line

20:20 – Two multithreading issues that can be solved using aspects

25:41 – Richard’s favorite example of AOP

36:43 – Static versus Dynamic AOP

40:08 – What’s next for PostSharp

Show the Code.

Gael also recorded a PostSharp demonstration with Carl on dnrTV. The shows on dnrTV are equally as entertaining as those on .NET Rocks! but with one very important difference: the code!

In this webcast-like format, guests are able to demonstrate technical topics in greater detail. That’s exactly what you’ll see in the recording with Gael as he dives deep into PostSharp – showing both common and advanced aspect usage.

dnrtv2

Watch the recording:  http://dnrtv.com/dnrtvplayer/player.aspx?ShowNum=0190

Highlights include:

02:10 – TraceArgumentsAspect : OnExceptionAspect

13:47 – TracePerformanceAspect : OnMethodBoundaryAspect

23:15 – CacheAttribute : MethodInterceptionAspect

45:57 – Undo/Redo

Special thanks to Carl and Richard for inviting Gael as a guest on both shows. And many thanks to all of you who watched the shows and provided feedback. Keep it coming.

Happy PostSharping!

-Britt

Source code used in these blog posts is available on GitHub

The last consulting gig I was at, we were required to use an SOA framework to interact with enterprise-level data. This framework was SOA almost entirely in name only, completely home-grown, and was something of a misguided "pet project" of the chief of IT, insecure, built on some shaky technologies, and was created to solve a problem that was either non-existent, or solvable by much simpler means. My team was very frustrated by this framework, but as consultants, you sometimes have to learn to pick your battles, go for the easy wins first, build trust, and then gradually tackle larger problems. However, this can be a very slow process that takes years, depending on the organization.

In the meantime, we were stuck with this framework that didn't really work well with multithreaded apps (like, for instance, a web server), would often crash, and sometimes just wouldn't work at all. We had to build a working app on top of this mess, and since we're trying to build trust in the organization, we needed to ensure a certain level of reliability in the app that we were delivering. What we put together was an elaborate system of transactions, retries, error handling, and logging. This worked pretty well, and usually hid all the unreliable nastiness from the end user. However, to use this system, changes had to be made to every method that would touch the SOA framework. Soon after, I got tired of having to add this to every method, forgetting to add it only to see a bug get filed, having to remind others, violating single-responsibility over and over again, etc. I wondered if there was a better way, and that's when I first became interested in using AOP, and PostSharp specifically.

Enough ranting about bygone projects though: you can use PostSharp to improve transaction management, regardless of whether you're building an app on a brittle home-grown SOA or not. Here's an example of an OnMethodBoundaryAspect that will give you the basics of a typical transaction (i.e. start, commit, rollback):

[Serializable]
public class TransactionScopeAttribute : OnMethodBoundaryAspect
{
	[NonSerialized] private ICharityService _charityService;
	private string _methodName;
	private string _className;

	public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
	{
		_className = method.DeclaringType.Name;
		_methodName = method.Name;
	}

	public override void RuntimeInitialize(System.Reflection.MethodBase method)
	{
		// in practice, the begin/rollback/commit might be in a more general service
		// but for convenience in this demo, they reside in CharityService alongside
		// the normal repository methods
		_charityService = new CharityService();
	}

	public override void OnEntry(MethodExecutionArgs args)
	{
		_charityService.BeginTransaction(); 
	}

	public override void OnException(MethodExecutionArgs args)
	{
		_charityService.RollbackTransaction();
		var logMsg = string.Format("exception in {0}.{1}", _className, _methodName);
		// do some logging
	}

	public override void OnSuccess(MethodExecutionArgs args)
	{
		_charityService.CommitTransaction();
	}
}

Really simple--there's nothing new here that we haven't seen in my other blog posts. Note that it's usually a good idea to rethrow the exception once you've handled the rollback, and that's done by default. Simply tag all the methods that need to take place in a transaction with [TransactionScope], and now you've saved yourself from writing the tiresome try/catch begin/commit/rollback code over and over with one simple aspect. And we've also gained a clean separation, and a great central place to put any logging code or special exception handling.

One thing that the above code doesn't address is the issue of "nested" transactions. In my example code, the Begin/Commit/Rollback transaction methods are just placeholders that don't actually do anything. In a production app, what you actually use to manage the transaction depends greatly on the underlying data access. If you are using ADO.NET connections like SqlConnection, ODBC, Oracle, etc, then you might want to use a TransactionScope (not to be confused with the name of this aspect) to make nesting easy (along with other things, like distributed transactions). If you are using some other persistence technology you may have to use a different API. However, remember to take the possibility of nested transactions into account (i.e. if there are two methods that both have the TransactionScope attributes applied to them, and one calls the other).

Now that we have a basic transaction aspect, go back to my original example: the service that I'm using will often fail and succeed inconsistently. What I need to add is a retry-loop: "if at first you don't succeed, try, try again." To do this, I can't use OnMethodBoundaryAspect anymore, I need MethodInterceptionAspect instead. Of course, I don't want to keep retrying forever, so I'll limit the number of retries with a hardcoded int value (or perhaps you'll want it in an App.Config or Web.Config file so you can change it without recompiling). I also know that a retry is only worth doing if a specific type of exception is thrown, say a DataException. On any other type of exception, it's worthless to retry (maybe the service crashed...again), so just log that exception for now and forget the retries (maybe in a production app you would want to automatically contact some on-call IT staff).

[Serializable]
public class TransactionScopeAttribute : MethodInterceptionAspect
{
	[NonSerialized] private ICharityService _charityService;
	[NonSerialized] private ILogService _logService;

	private int _maxRetries;
	private string _methodName;
	private string _className;

	public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
	{
		_methodName = method.Name;
		_className = method.DeclaringType.Name;
	}

	public override void RuntimeInitialize(System.Reflection.MethodBase method)
	{
		_charityService = new CharityService();
		_logService = new LogService();
		_maxRetries = 4;            // you could load this from XML instead
	}

	public override void OnInvoke(MethodInterceptionArgs args)
	{
		var retries = 1;
		while (retries <= _maxRetries)
		{
			try
			{
				_charityService.BeginTransaction();
				args.Proceed();
				_charityService.CommitTransaction();
				break;
			}
			catch (DataException)
			{
				_charityService.RollbackTransaction(); 
				if (retries <= _maxRetries)
				{
					_logService.AddLogMessage(string.Format(
						"[{3}] Retry #{2} in {0}.{1}",
						_methodName, _className,
						retries, DateTime.Now));
					retries++;
				}
				else
				{
					_logService.AddLogMessage(string.Format(
						"[{2}] Max retries exceeded in {0}.{1}",
						_methodName, _className, DateTime.Now));
					_logService.AddLogMessage("-------------------");
					throw;
				}
			}
			catch (Exception ex)
			{
				_charityService.RollbackTransaction();
				_logService.AddLogMessage(string.Format(
					"[{3}] {2} in {0}.{1}",
					_methodName, _className,
					ex.GetType().Name, DateTime.Now));
				_logService.AddLogMessage("-------------------");
				throw;
			}
		}
		_logService.AddLogMessage("-------------------");
	}
}

Yikes, this one looks a little scary! But don't worry, let's break it down into smaller, bite size pieces. CompileTimeInitialize, and RuntimeInitialize shouldn't look much different: they're just storing the class and method names, initializing the services we'll need, and initializing the maximum retries allowed.

For OnInvoke, let's break it down into the four possible scenarios that it handles:

  1. The method runs without exception and everything works. This is the happy path, and that's the block of code inside of the try { }
  2. The method fails with a DataException, and we haven't reached the retry limit yet. This is the code in the first part of the if/else inside the first catch { }.
  3. The method fails, and now we've exceeded the maximum number of retries. This is the code in the second part of the if/else inside the first catch { }.
  4. The method fails, but it's some unknown exception where a retry won't help. This is the block of code in the second catch { }

In my sample app, I've made a CharityService whose methods will fail almost 50% of the time due to a DataException, succeed 50% of the time, and throw a non-DataException exception 1-in-50 times. When you run the sample application, you'll be able to see in the log window how many retries are taking place (if any), along with the begins, rollbacks, and commits. Finally, the user will only see a MessageBox error message when the number of retries is exceeded, or when some non-DataException exception is thrown.

Screenshot of sample app

By using this aspect, you can give your application an improved level of robustness. The aspect code is getting large, but manageable. If it needs to get much more complex, it might be time to refactor: consider putting each case into its own class, and just use the OnInvoke method to establish a general flow.

The real-world example I gave at the beginning of this post is (hopefully) a rare case, but even the best of architectures can fail from time-to-time. You don't have to dread handling those situations when you are using PostSharp to implement SOLID principles in your application.

Matthew D. Groves is a software development engineer with Telligent, and blogs at mgroves.com.