Dependency injection using compile-time weaving? [closed]
Asked Answered
L

1

13

I just tried to learn about PostSharp and honestly I think it's amazing.

But one thing that it is difficult for me how a pure dependency injection (not service locator) cannot be done in PostSharp aspects, perhaps in my understanding as a result of compile time weaving.

Came from PHP background, Symfony has JMSAopBundle which still allows dependency to be injected to it's Interceptor.

Does .Net have some libraries with same capability?

Or am I missing something with PostSharp?

Levigate answered 11/5, 2014 at 9:16 Comment(8)
If you're already interested in pure Dependency Injection, PostSharp is completely redundant. Instead, you can pick a DI Container that supports run-time Interception: https://mcmap.net/q/128331/-logging-aspect-oriented-programming-and-dependency-injection-trying-to-make-sense-of-it-allBindweed
And would go even further by saying that run-time interception is redundant.Ithyphallic
@Ithyphallic I think it's more of flexible rather than redundant. Compile time interception just won't wait for any runtime initialization of anything.Levigate
@MarkSeemann PostSharp extends the C# and VB languages to allow for language extensions, like Nemerle, Boo, Scala and other do. Better programming languages allow for coding at a higher level of abstraction, in cases where there is no good OOP solution for it. There is not anything you could do with these tools you could not do with plain C#, but you get more productive using a language of a higher level of abstraction. If you accept the argument of redundancy, C# is redundant to C++, which is redundant to C, which is redundant to assembly, which is redundant to microcode.Alvin
@GaelFraiteur There's currently a very successful trend that less is more. The future of programming may very well lie in taking away features. As an example, for a long time we've understood that code without GOTO statements is better than code with GOTO statements. Similarly, the reason why we're seeing a renewed interest in Functional Programming is because we're beginning to understand that code bases without mutability are better than code bases with mutable state. While I agree that good abstractions are desirable, I disagree that adding more features to a language achieves that goal.Bindweed
CodeCop is a good alternative to intercept methods. Here is a wikiFerial
I don't think that dependencies injection should be done using an aspect oriented approach. You can using dependency injection frameworks for that, or simply use constructor injection (including factory methods injection via constructor) without a framework.Tavern
Fody, Extensible tool for weaving .net assemblies github.com/Fody/FodyBeera
I
16

I don't think you're missing anything here and the limitation is indeed the result of using compile time weaving.

Although I think compile time weaving tools have its place in software development, I feel that they are often overused. Often I see them being used to patch flaws in the application design. In the applications I build I apply generic interfaces to certain architectural concepts. For instance, I define:

  • an ICommandHandler<TCommand> interface for services that implement a certain use case;
  • an IQueryHandler<TQuery, TResult> interface for services that execute a query;
  • an IRepository<TEntity> interface as abstraction over repositories;
  • an IValidator<TCommand> interface for components that execute message validation;
  • and so on, and so on.

This allows me to create a single generic decorator for such group of artifacts (for instance an TransactionCommandHandlerDecorator<TCommand> that allows running each use case in its own transaction). The use of decorators has many advantages, such as:

  • Those generic decorators are completely tool agnostic, since there is no reference to a code weaving tool or interception library. PostSharp aspects are completely dependent on PostSharp, and interceptors always take a dependency on an interception framework, such as Castle.DynamicProxy.
  • Because a decorator is just a normal component, dependencies can be injected into the constructor and they can play a normal role when you compose your object graphs using Dependency Injection.
  • The decorator code is very clean, since the lack of dependency with any third-party tool.
  • Because they're tool agnostic and allow dependency injection, decorators can be unit tested easily without having to revert to special tricks.
  • Application code that needs cross-cutting concerns to be applied can as well be tested easily in isolation, because decorators are not weaved in at compile time. When decorators are weaved in at compile time, you're always forced to do a integration style of testing of your application code, or need to revert to special build tricks to prevent them from being applied in your unit test project.
  • Decorators can be applied dynamically and conditionally at runtime, since there's no compile time code weaving going on.
  • Performance is identical (or even faster) than with code weaving, because there's no reflection going on during object construction.
  • There's no need to mark your components with attributes to note that some aspect must be applied. This keeps your application code free of any knowledge of such cross-cutting concern and makes it much easier to replace this.

A lot has been written about this kind of application design; here are a few articles I wrote myself:

UPDATE

Decorators are great, but what I like about AOP is it's concept of advice and join points. Is there a way to simulate the same capability with decorator? I could only think of reflection right now.

A Join Point is a "well defined location within a class where a concern is going to be attached". When you apply AOP using decorators, you will be 'limited' to join points that are on the method boundaries. If however you adhere to the SRP, OCP and ISP, you will have very thin interfaces (usually with a single method). When doing that, you will notice that there is hardly ever a reason for having a join point at any other place in your classes.

An Advice is a "concern which will potentially change the input and/or output of the targeted method". When working with decorators and a message-based design (the thing I'm promoting here), your Advice needs to change the message (or replace the complete message with altered values) or change the output value. Things aren't that much different than with code weaving—if you apply an Advice, there must be something in common between all code the Advice is applied to.

Ithyphallic answered 11/5, 2014 at 11:7 Comment(4)
Amazing blog, but I do have a question though. Decorators are great, but what I like about AOP is it's concept of advice and join points. Is there a way to simulate the same capability with decorator? I could only think of reflection right now.Levigate
I like this solution. However, it is important to know the strengths and weeknesses of every solution. The biggest weakness with this solution is readability. To any developer with <5 years experience, they are going to look at the AddUserContextCommandHandlerDecorator<TCommand> object and feel overwhelmed. If your entire project is filled with such, a developer might get lost. They are constantly going to fail to use the decorators correctly, which requires constant changes after code reviews by the dev lead. Whereas other solutions provide far better readability.Cytoplast
@Rhyous: Please keep in mind that "<5 years" devs will fail with every architecture that is provided to them, in case there is no good documentation or mentor/architect, guiding them and reviewing their code. However, I experienced this proposed design to actually work much better with junior devs than any alternative I previously worked with, as this design pushes devs in a certain way of working. I experienced the amount of RFC to rewrite and redesign code I had to place on the backlog after returning from holiday, was way less using this design.Ithyphallic
My personal experience is that junior devs have little problem understanding this design. I noticed junior devs to generally more easily adapt this design, where older devs found it harder to change the way they had to write their code. Note that the amount of decorators in your solution will be minimal, which is one of the major benefits of this design. Try explain any dev to write a PostSharp attribute. That’s much harder. Although I let juniors write decorators, I certainly want to review that code, but BTH, I would need to do that with any other design as well.Ithyphallic

© 2022 - 2024 — McMap. All rights reserved.