After working with Spring Boot for a couple of months now, one thing that I find lacking in ASP.NET Core (C#) is Aspect Oriented Programming (AOP).
In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a “pointcut” specification, such as “log all function calls when the function's name begins with ‘set’". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code, core to the functionality. AOP forms a basis for aspect-oriented software development. – Wikipedia
There are a couple use cases where AOP can be used:
- Logging or Auditing
- Transactions management
- Security (rarely)
All the code in this blog post is demonstrated in this repository.
AOP in C#.
It's already possible to do AOP in C#, a quick search in Google will show you how:
MarshalByRefObject. Technically, it can be used both locally and remotely, which is very nice until you understand that all your target objects must inherit
MarshalByRefObject. That alone drives people to not even consider it.
The better way
Thankfully, there is a better way to create proxies of objects in C#, using Castle.DynamicProxy.
Castle DynamicProxy is a library for generating lightweight .NET proxies on the fly at runtime. Proxy objects allow calls to members of an object to be intercepted without modifying the code of the class. Both classes and interfaces can be proxied, however only virtual members can be intercepted. – Castle Project
Using Castle's dynamic proxies, you can create proxies of abstract classes, interfaces (while providing the implementation) and classes (only virtual methods/properties).
As an example, let's say we're making a server that handles blog posts:
Usually, you would register
IBlogService with the implementation
BlogService, and everything will work well. But now, you want to proxy the interface and do something whenever a method is called.
First, we need to create an interceptor that intercepts method calls, just like
Then, we'll create the proxy object using a generator:
We have now created a proxy object that implements
IBlogService and has the inner implementation
BlogService. Whenever an interface method is called, the
LoggingInterceptor.Intercept method will be called and when it calls
invocation.Proceed(), the implemented method in
BlogService will be called.
Using it in ASP.NET Core
The idea here is to always serve proxies of services using the IOC Container of ASP.NET Core. Castle Project does have its own IOC Container Castle Windsor which makes injecting proxies much easier, but we won't use it for now.
First, we'll add a simple dependency to our
LoggingInterceptor to show how we can handle them using DI. In reality, most of your interceptors will require one or more dependencies.
Second, we'll register a singleton
ProxyGenerator and all the interceptors we're working with (
LoggingInterceptor for now):
Lastly, we'll register everything:
Here's how it works:
- We register the implementation (e.g.
BlogService). This is because the implementation might have dependencies that need to be resolved using DI, (there might be a better way).
- Whenever the interface is asked for:
- We get the instance of
- We get an instance of the implementation.
- We get all the registered interceptors.
- We create and return a proxy of the interface with the inner implementation and the interceptors.
- We get the instance of
Now, whenever we ask for
IBlogService, we'll receive a proxy object that passes through all the interceptors, and then call the actual methods in
Unfortunately, this isn't very easy nor straightforward to do in ASP.NET Core compared to Spring, but we can easily turn this into a simple “framework”. We can hook into specific methods using Castle's DynamicProxy and do more advanced things.
I hope this helps some of you & Happy Coding!