Here's a quick example of how to write a caching aspect with Castle DynamicProxy.
First, let's write an implementation and a service worth caching:
Next, you need to decide where to cache the results. If you're using a web app, maybe try ASP.NET's Cache object. If you're writing an Azure app, try AppFabric caching. You can cache things in a database, a text file, whatever is appropriate for your application.
For this simple example, I'm going to cache everything in memory, in a static Dictionary object. Also, nothing that goes into my cache will ever expire or be invalidated. Once it's cached, it's cached forever. Also, it's not thread safe. Not a very useful cache in a real app, so let me just be clear: do not use this in a production application. It's only for demonstration: whatever you use for caching is up to you, I'm just demonstrating the aspect part.
I'm generating the cache key by appending the argument values to the method name. So if you call MyMethod("test") and MyMethod("test2"), that's two different keys that will cache two different results. This might work for you, or you might need a more complex GenerateCacheKey method (something that uniquely identifies objects, perhaps).
Next, I wire up my IoC container to return MyService when asked for an implementation of IMyService. But I also have to make sure that my IoC container (StructureMap in this case) applies the caching interceptor to it.
I put it all together in a simple console app:
And here's the result:
When deploying this site for the first time and actually telling people to come here, I learned that I had made a pretty big mistake. Appropriately (or perhaps tragically), I made a mistake in one of the cross-cutting concerns of my own site.
I am using StructureMap to wire up all my dependencies, and 90% of these dependencies are just done WithDefaultConventions. But there is one dependency, IDbConnection which I wired up by hand, like so:
If you can spot the mistake here, then congratulations, you are officially a better developer than me (which is no high accolade). Here's what I should have done:
And then, in Application_EndRequest, I just call ReleaseAndDisposeAllHttpScopedObjects() on ObjectFactory. This means each of the SqlConnections will be bound to a request, and then disposed of when the request is complete. Because I wasn't doing that before, each of the connections would stay open. I didn't notice this when developing the site, because I was continually rebuilding and redeploying.
This may not be strictly speaking AOP, but it's certainly a cross-cutting concern, as almost every request makes use of the IDbConnection to use Dapper, and using StructureMap to manage that dependency saves me from writing a bunch of boilerplate "new SqlConnection" in all my repositories.
Lesson learned: if this site was actually mission critical to a business, I would've been in hot water. I should've been testing with a larger number of requests over a longer running application in an environment similar to production. Fortunately, since I didn't have a bunch of repetitive boilerplate, it was very easy for me to make the fix, once I figured out what the problem was.