Why aspect-oriented programming in C# is pointless

Sdílet
Vložit
  • čas přidán 8. 12. 2021
  • Check out my courses: dometrain.com
    Use discount code YTUNIT2 at checkout for 15% off
    Become a Patreon and get source code access: / nickchapsas
    Hello everybody I'm Nick and in this video I will explain what aspect-oriented programming, or AOP for short, is in the context of C#. I will explain how you can implement it and why it's something that could be of use and also show you why ultimately it's not something I recommend using.
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasGitHub
    Follow me on Twitter: bit.ly/ChapsasTwitter
    Connect on LinkedIn: bit.ly/ChapsasLinkedIn
    Keep coding merch: keepcoding.shop
    #csharp #dotnet #aop

Komentáře • 179

  • @nickchapsas
    @nickchapsas  Před 2 lety +39

    Few things I didn't mention in the video
    1. You can also create interception scenarios in non-API calls using MediatR and its PipelineBehaviors. I have a video on that here: czcams.com/video/2JzQuIvxIqk/video.html
    2. In the last example you don't have to create one implementation per interface you are creating. You can have a generic interface, implement the logic you want there, and then use DI registration by convension to decorate your methods dynamically with something like Scrutor.
    3. If you are doing UI stuff and you need INotifyPropertyChanged then this might make sense to you. I don't do UI stuff so I don't know.
    4. interception and decoration are independent patterns and using them doesn't mean that you suddenly are doing AOP. AOP however is using those patterns to be implemented.
    5. If it works for you that’s totally fine. Don’t let my video discourage you. You are not wrong for liking it and I’m not wrong for disliking it.

    • @gunnarliljas8459
      @gunnarliljas8459 Před 2 lety

      How would we get around having to create one implementation per interface in the last example? I mean, it can be done, using e.g. DynamicProxy (or indeed PostSharp), but I guess you had something else in mind?

    • @Karysff
      @Karysff Před 2 lety

      Came here to say you could use Scrutor, but apparently you're a smart dude and you know that. I however can't find a ready made example of a generic logging class, that would just wrap any interface. Maybe I'm missunderstanding something, but if not, please share a link to an example.
      Ideally I imagine something like decorating methods with `[DurationLogging]` attributes, scanning DI registered classes for methods with these attributes generating wrappers at runtime. Something along the lines of System.Reflection.Emit. It would also mean you could then inject a logger of your own in to this contraption. This sounds messy, but if the complexity could be done in a separate nugget would be a really handy.
      Or just use PostSharp like here -> dotnetcoretutorials.com/2021/02/05/supercharged-net-core-logging-with-the-postsharp-logging-framework/

    • @gunnarliljas8459
      @gunnarliljas8459 Před 2 lety

      @@Karysff My point exactly.

    • @chrisnewey
      @chrisnewey Před 2 lety

      MediatR works great for this scenario

    • @Karysff
      @Karysff Před 2 lety

      @@chrisnewey adding MediatR when you don't need a mediator to use just for logging isn't great

  • @evanboltsis
    @evanboltsis Před 2 lety +8

    The key that Nick did not show was 4202A873-1917-4A20-ABB1-8C4936FE5069

    • @nickchapsas
      @nickchapsas  Před 2 lety +7

      You are my new best friend

    • @agsystems8220
      @agsystems8220 Před 2 lety +1

      Have to admit I snorted a bit at not even pretending the key was legitimately obtained. Think I would have gone with "I obtained a key for this video" and left it at that, maybe directed people to the github key leaks video as an "entirely unrelated tangent"... :P

  • @AB-jt6ic
    @AB-jt6ic Před 2 lety +5

    Great video Nick. I first read of AOP over 10 years ago, and recently wondered whatever happened to it. I would love to see DI support built into Attributes.

  • @user-il5fc2qk6t
    @user-il5fc2qk6t Před 2 lety +4

    I totally agree with your conclusion at the end of the video, but not with video title. All demonstrated examples are part of aspect oriented paradigm. AOP is about separation of cross-cutting concerns. And there are many different ways you can achieve this separation. Decorating is the most elegant way for sure. Thank you.

    • @nickchapsas
      @nickchapsas  Před 2 lety +2

      Decoration and interception are independent patterns. Not all interception and not all decoration is AOP. It’s just interception and decoration. That’s the point people are missing. AOP is using those patterns but the patterns themselves aren’t AOP.

  • @hernanar3647
    @hernanar3647 Před 2 lety +29

    I'm developing a framework based con CQRS with MediatR and other things, for aspects like Logging, Validation and Stopwatch (At least that are the aspects that I implement), I use a IPipelineBehavior with customization via config, so it is modular and flexible. MediatR is a amazing libary

    • @nickchapsas
      @nickchapsas  Před 2 lety +18

      Originally I was planning to show Scrutor for decoration and MediatR for Pipelines but I scrapped it because I didn't wanna make it library specific, but yeah, I am using MediatR Pipelines as well for the cross cutting concerns.

    • @buriedstpatrick2294
      @buriedstpatrick2294 Před 2 lety +2

      Was about to comment this. It is interesting how you can sort of mimic a poor man's version of the behavior just using native C# though. Great for PoC.

  • @conway9214
    @conway9214 Před 2 lety +5

    Autofac actually has support for interceptors, which can allow us implement AOP without needing to pay for post sharp. Dependency injection is also possible.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      Interceptors aren't AOP. Interceptor is an independent pattern. AOP is using interceptors as one of it's implementation approaches, but the pattern itself is just a pattern. You also really don't need Autofac or any third party IoC library.

    • @conway9214
      @conway9214 Před 2 lety

      Noted, thanks for the reply!
      I have just joined the c# world few months ago, been learning a lot from your channel 👍

    • @emreaka3965
      @emreaka3965 Před rokem

      @@conway9214 You already did not call interceptor AOP. You said the same thing Nick said: "Autofac actually has support for interceptors, which can allow us implement AOP".

  • @rsfurlan90
    @rsfurlan90 Před 2 lety +5

    I like using the decorator pattern with a proxy (Castle Windsor is great for that) so I can inject dependencies as required. Each aspect will behave like a "onion layer" - a wrapper around the concrete implementation, as long as it implements the same interface. Great content, thanks for sharing!

    • @pro100tom
      @pro100tom Před 2 lety

      I like using decorators as well. However I am struggling with using one when it comes to adding the raising of a custom event...

  • @kazepis
    @kazepis Před 2 lety +1

    Great video Nick! Congrats. Instead of the reverse DI anti pattern you did not want to talk about, can we use a logger such as Serilog which provides static access to the actual logger?

  • @christianista
    @christianista Před rokem

    Is it possible to log each method and the parameters ?

  • @barmetler
    @barmetler Před 2 lety +5

    Using copilot to get keys for this stuff is so funny, I can't wait to be accepted into the program :(

  • @shurale85
    @shurale85 Před 2 lety

    I assume this library use reflection to call intercepted method, doesn’t it? Is there any simple code of how I can write my own interception?

  • @BrianKapellusch
    @BrianKapellusch Před 2 lety +1

    What about AOP interceptors vis an IOC container like structuremap? You can inject via IOC into those aspects I thought

  • @ddanielewski
    @ddanielewski Před 2 lety

    How did you get PostSharp to work with Rider?

  • @tofraley
    @tofraley Před rokem +4

    Nice video. I'd like to see your take on Metalama, PostSharp's successor. The creator addressed this DI issue on the Visual Studio Toolbox show. His demo basically relied on some dynamic programming. But it did essentially make it work with DI, and was testable.

  • @tbddevelops
    @tbddevelops Před 2 lety +5

    I've been trying to consider how to introduce metrics into an application so as to keep it structured so that we don't just inject ILogger and end up with a billion log statements that have no value. I've often considered AOP as a way of doing this, but then I worry about the magic code that nobody wants to go near because it's "expert level". I think these approaches are definitely easier to understand for most development. Seeing the comment earlier about MediatR, I would be interested in seeing how you utilize MediatR pipelines effectively. Thank you for the video.

    • @nickchapsas
      @nickchapsas  Před 2 lety +7

      I actually have that video already. Check it out here: czcams.com/video/2JzQuIvxIqk/video.html

    • @tbddevelops
      @tbddevelops Před 2 lety +1

      @@nickchapsas Thank you. That was very helpful. I've only recently come across your videos, I'm catching up.

  • @benjaminfortune2707
    @benjaminfortune2707 Před 2 lety

    At around 8:25, is that basically a static, service locator? I've never seen an example I've really understood about why using a service locator causes problems, despite that being said by nearly anything I've ever read about it. Like, if you don't have access to a constructor for whatever reason (e.g. here with attributes), what's wrong with using a service locator if it's configured to resolve the same dependencies that constructor injection does?

    • @nickchapsas
      @nickchapsas  Před 2 lety +2

      The problem, other than testability, is that intent is hidden because if the service required isn't in the constructor or the method then you can locate absolutely anything and really break stuff. Basically it's intent and behavior obfuscation and you leak things that shouldn't be leaked. Also, if you wanna resolve a new service and you add a constructor to your class, you are kinda forced to not only fix your breaking tests but also write new ones for the new added behavior. With the service locator you don't need to do any of that, which leads to worse code and developer behavior.

  • @Rajeshsingh-ws5th
    @Rajeshsingh-ws5th Před 2 lety

    URGENT: Controller level any attribute runs but what about at domain or data access layer attribute or intercepter? Please discuss.

  • @peterhevesi6571
    @peterhevesi6571 Před rokem +1

    There is now a clean rewrite of PostSharp called Metalama, which does supports Dependancy Injection

  • @dmitriyrogovoy
    @dmitriyrogovoy Před rokem

    Nick, cant we use MediatR behavior for this instead?

  • @nikola7377
    @nikola7377 Před 2 lety

    If we manually write stopwatch on every method we want we don't respect DRY, but the last solution doesn't respect DRY even more? We have new service etc, and we have to repeat it for every method, what is benefit then?
    Can we use this approach of postsharp with your trick with dependency injection using source generators?

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      You don't have to manually create the Stopwatch on every method. You can have the stopwatch in one decorator, create a generic interface and implement that and then register all your interfaces by convension with something like Scrutor.

  • @Nekroido
    @Nekroido Před 2 lety

    Thanks for the insight! This approach reminds me of how I used attributes in Python, those are essentially wrappers around methods. Really powerful stuff, as I could have simple controller methods that return objects, collections of objects, throw exceptions, and a wrapper would transform everything into neat JSON responses. Loved it so much, damn...

  • @Jashobantac
    @Jashobantac Před 2 lety

    Thanks Nick for adding something on AOP. I had been requesting something on AOP for quite some time.

  • @sirdondaniel
    @sirdondaniel Před rokem

    What I like about PostSharp is that you can decorate your class with an attribute OnExceptionInEveryMethodAttribute : OnExceptionAspect, and run a piece of code every time an exception is being thrown by any public method. And one can find an acceptable workaround for the dependency injection.

  • @stephenyork7318
    @stephenyork7318 Před 2 lety +1

    I wrote a CQRS library (not using MediatR) which uses Decorator pattern and the ioc decorator registration methods found on simple injector or AutoFac to wire everything up. Requesting an IHandleCommand type give you the full pipeline of decorators and achieves what you’re describing.

  • @andreasdaxer1168
    @andreasdaxer1168 Před 2 lety

    Hi Nick, a nice application of the Proxy pattern, that I also used before. One thing that struck me though is, that the DI works like that. There seems to be some intelligence in the Asp.Net DI system. Otherwise I would expect a stackoverflow or a thing like that, when you inject IWeatherService into the ctor of the Logging proxy, as this class is registered as IWeatherService itself.
    BTW, the Harmony package would be a way of intercepting methods in runtime, but I did not try to do DI with it (only played around a bit). Did you look into that already?

    • @georgehelyar
      @georgehelyar Před 2 lety +2

      The inner implementation was registered as concrete WeatherService for this reason. If the IWeatherService required an IWeatherService it would stack overflow.
      You can have multiple registrations of the same interface, last registration wins, and you can get an enumerable of all of them, but you can't have one that calls itself.
      ActivatorUtilities also exists and can help in some more advanced cases.

  • @seangwright
    @seangwright Před 2 lety +1

    Decoration with generics is my preferred approach. It can be cumbersome if your interfaces don't follow Single Responsibility Principle and have too many methods (the decorating type needs to implement all of them). But if you can design APIs with a simple (ideally single method) interface, then decoration is very elegant for cross cutting concerns.
    I use this kind of decoration for caching, logging, error handling, rating limiting, ect... ect...

  • @JochenZeischka
    @JochenZeischka Před 2 lety +2

    Hi Nick, thanks for your video.
    It explains AOP quite well. However, the demo implementation with a single LoggingAttribute is where it gets you in trouble.
    It's better to define two classes to support your aspect:
    - one to declare where you are going to apply it (attribute with compile time constants is perfectly fine)
    - and one behaviour class, which actually implements the aspect
    The implementation class is not an attribute and thus can enjoy all the goodies of dependency injection.
    The DI container then needs to know how to create a behaviour pipeline based on the attributes found on a class. Usually, the DI container constructs some kind of dynamic proxy for that.
    Quite complicated behind the curtains, but it covers all the things you wished (ease of applying an aspect on any class / method / ...)
    Microsoft's Unity container supported this through the interception extension. Pretty sure there must be other DI implementations which also support this.
    Thanks for all your videos. I'm impressed by the wide variety of topics you bring us!

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      Hey Jochen, thanks for your comment. That sounds like a better way to go about actually. But then at that point, it is even worth it. In reality, the only usecase I ever had for AOP was either logging and/or metrics collection. Both, I can do with either a filter, middleware or MediatR's PipelineBehaviors. So maybe I am just spoiled by the options I have to achieve the same thing, and my usecases. I don't do front-end so i don't know if it's more relevant one that front.

    • @JochenZeischka
      @JochenZeischka Před 2 lety

      Hi @@nickchapsas, my hands-on experience is also on the backend. And I haven't used "real" AOP since 10 years, since indeed, we have multiple options for handler/behaviour pipelines (MVC filters / HTTP client message handlers / NServiceBus behaviours / ...)
      But of course, for the community it is good to know that DI-based AOP solutions does exist. Just don't expect the attribute to implement the behaviour.

  • @slyp05
    @slyp05 Před 2 lety

    What editor are you using?^^

  • @JohnZakaria
    @JohnZakaria Před 2 lety +2

    Decorator pattern remade?

  • @if07012
    @if07012 Před 2 lety +1

    Hi nick, your video always very interesting
    Did you try dynamic proxy from Castle Windsor? I see we can intercept the method using that, is it recommended to replace postsharp if we want a free library ? :D
    I want to try build app, but I see if we are using AOP, we can make some function more simple like logging, auto commit, and etc

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      I'd probably use Fody to replace PostSharp but really you don't need any of that. Proxies with Windsor are fine but you are creating runtime types and that will have a performacne hit to your app. My approach is usually with MediatR and PipelineBehaviors.

    • @alansinoracki8508
      @alansinoracki8508 Před 2 lety +2

      @@nickchapsas i think the performance hit of dynamic proxies is not that big but the worse thing is that we need virtual members for no other reason than to allow dynamic proxy to work.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      I will have to run benchmarks to get the exact numbers

    • @alansinoracki8508
      @alansinoracki8508 Před 2 lety

      @@nickchapsas Yeah thats the best thing to do. But IIRC the overhead of calling proxied method is just a tad biger than calling method by interface. The most intense is creation of the proxy type and dynamic assembly but it can and should be cached anyway

    • @tamirben1
      @tamirben1 Před 2 lety

      We have been using Castle for logging and metrics and it does the job.
      However, making it work nicely for async functions was not fun. Probably have issues with yields as well...
      But in all actuality we added AOP to the code base mainly because the team lead was a bit obsessed with it from his time writing Java. No one else thought it was necessary to do it that way

  • @jasoncox7244
    @jasoncox7244 Před 2 lety

    I've been looking for this for years, and got really hopeful. I'm looking for an equivalent to the `IAttributeFilter` interface but for class libraries. It would be really convenient to be able to decorate with something to intercept a method call that way in a general setting.

  • @max-S
    @max-S Před 2 lety

    I would really appreciate a video about Performance testing in a CI/CD pipeline (which itself might vary quite a bit in performance)

  • @apex761
    @apex761 Před 2 lety +2

    One of the things that evolved out of dependency injection is AOP, so IMO AOP is still useful. Anyway in your example you can implement DI its just a matter of configuration. Also attributes are not the best way to use AOP, there is a way to do fluent configuration for most AOP frameworks. Now, on another note, AOP came out around the same time as DDD and it was popular in crating low coupling in your code, at the time this was great. DDD has taken over, for other reasons, but it can get you low coupling, SOLID helps with this as well. To me I treat AOP more like a design pattern than like a "language", that part was confusing to me to.

  • @johnspencer772
    @johnspencer772 Před 2 lety

    Really love your content!! Sometimes things are over my head!!! But, that is OK -- that just means I need to learn. Which is a good thing!!
    Just a way of saying Thanks for sharing your knowledge!!

  • @dksovfen
    @dksovfen Před 2 lety +1

    Am I missing something?
    You didnt show how to do DI on the attribute, it was just another attribute implementation but still no DI and with less use cases because of the need of I(Async)ActionFilter.
    Correct me if Im wrong but I(Async)ActionFilter is only usable with controllers/or atleast mvc stuff? I cant use it with WPF stuff?
    The rest makes sense, and thanks for a good video, just dont get the first part.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      There are two ways to do it. One is a bit hacky and I don't recommend it but you can use the HttpContext.Services.GetRequiredService(). The other way is to remove the Attribute part and do dependency injection from the constructor normally. Then you can use it as an attribute with the [ServiceFilter(typeof(YourActionFilter))] as long as you've registered YourActionFilter in DI with AddScoped.

    • @dksovfen
      @dksovfen Před 2 lety

      @@nickchapsas Would have liked this to be a part of the video, thanks for the answer tho :) And if this works for the IActionFilter attribute why dont you think it fits with the postsharp implementation?

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      ​@@dksovfen DI doesn't properly work with the ActionFilter as an attribute, and I don't think it should be used. It only works with the ServiceFilter attribute which wraps the ActionFilter itself. That, I recommend, because you can do dependency injection properly and you don't need to do any dodgy service resolution.

  • @LuizHenriqueMiranda
    @LuizHenriqueMiranda Před 5 měsíci

    I think it's a good time to review this video. Metalama (from the company that created postsharp) seems to be able to solve the issues you pointed in this video. I'm considering buying a license and would love to hear what you think about it.

  • @danijelzg001
    @danijelzg001 Před 2 lety

    @Nick make a video about monitoring shared folder (smb, cifs) for file changes (polling, file system watcher etc..) through docker with linux image, is it possible, performace, alternatives to polling

  • @ninilab
    @ninilab Před 2 lety +1

    My CS AOP prof probably would disagree with you :)
    Back then I didn't even understand what's AOP was about, how complicated presented it was.
    In nodejs projects we use simple decorators, e.g. for incrementing metrics where applicable.

  • @Alex-gj2uz
    @Alex-gj2uz Před 2 lety

    Great content also to repeat and see concepts. But isnt your final solution loosing the point of what the initial promise of AOP resp. the Attribute was, avoiding to spread the logger or mettrics code all over your classes?
    To an outsider this looks now more confusing i would say, if you would just have it done the old fasioned way? (Instanciate the logger where it makes sense, later on use the incjected one in your implementation?)
    Cheers

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      That's a valid point. The original draft had me show how you can acutally solve that but the video was getting too long. You can achieve the same generic approach with just one class using MediatR's Pipeline behavior. H have a video on that here: czcams.com/video/2JzQuIvxIqk/video.html

  • @codewkarim
    @codewkarim Před 2 lety

    I think it's also possible to achieve some scenarios using Mediator since it creates its own pipeline (but again it's a package).

  • @andreilastochkin5133
    @andreilastochkin5133 Před 2 lety

    there is even more clear solution to preserve DI objects for using in the interception attr without interfaces etc, based on ConditionalWeakTable.
    it will looks aprox like:
    class MyController
    {
    [Preserve]
    public MyController(ILogger logger) {}
    [MyInterception]
    void MyMethod() {}
    }

  • @InstrumentalsDaily
    @InstrumentalsDaily Před 2 lety +3

    I immensely appreciate your videos Nick

  • @SpaceShot
    @SpaceShot Před 2 lety +1

    This technique, if I understand right, wraps the implementation with the logging around the implementation. It really starts to feel like what I see some functional programmers do. They know that the business logic takes certain inputs and returns certain outputs, so the logging function wraps (or binds) to the business function (without any knowledge of logging or cross cutting concerns) and then as a part of program composition (not terribly unlike DI or more accurately, the old Unity application block) they bind up the logging with the logic, but with a lot less magic. I like the approach and have wondered if it mapped to C# for awhile. I think what you've done here is hard to swallow because it is a bit out of the norm from all of the years (decades?) of tutorials and documentation. I mean, it can take work to get people to move logic out of controllers! How do you smooth this over when you work on a team or at a business and you get pushback that "you're making it complicated"? Great video Nick... I like your ability to open a discussion not just on features, products, frameworks, but on code itself. You don't present it as high level astronaut architecture, but as something that can really help you stay productive.

    • @nemanja.djordjevic
      @nemanja.djordjevic Před 2 lety +1

      This, as you called “technique”, is nothing new. It is classical decorator pattern from GoF book.

  • @noodle-eater
    @noodle-eater Před 2 lety

    Cool, a few month ago i was searching and interesting with this topic but not dig deeper yet

  • @leonardomoreno23
    @leonardomoreno23 Před 2 lety

    Nice videos. Btw Not sure about built-in DI engine but simpleinjectior fwk allows you to register decorators in an easier way.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      For the built in one you can use Scrutor which also add similar decoration behaviour very easily

  • @YariRu
    @YariRu Před 2 lety

    Now I get strong flashbacks from 2008 about PostSharp. Used that for logging and transaction context

  • @ristopaasivirta9770
    @ristopaasivirta9770 Před 2 lety +2

    License key for thousand lines of code?
    Fine...
    _proceeds to remove all line breaks_

  • @sandromagalli8587
    @sandromagalli8587 Před rokem

    Ok so i will mention one example where this can be usefull, im a junior dev btw...
    In Blazor Server, you have a page and inject a service with the dbContext of Entity Framework and use its method to persist something to the database that is on the server.
    Now what if i want to make sure those methods on the service require authorization?
    You can have authorized attribute on the component and not show the page, you can use the AuthorizeView to hide stuff to unauthorized users...buttons etc.
    In other cases, like some kind of dashboard or a calendar, a user should see an element but not be able to drag it around and drop it.
    You can do all the checks on the UI to prevent such actions but in the end you also have to make sure that when calling the service method the user is authorized.
    So instead of getting the user info and checks his roles etc, it would be nice to just stick a decorator on the method like it was an api endpoint and return an error if it is not authorized.
    A probable downside is that by sticking the attribute in there you cant really reuse the logic for another app, like a mobile one.
    Basically where should one put the authorize logic of api controllers when using blazor server?
    I know the Blazor connection is always alive and you are not making http calls so I get it's a different situation than when the normal authorize attribute with controllers.
    The last example Nick shows it's good enough, tho.

  • @ArgeKumadan
    @ArgeKumadan Před 2 lety

    The dependency resolver for .net core or .net 6 doesn't support the interceptors but usually IOC containers support it, u don't have to use Attributes (: and AOP on compile time is just one way of doing it. There are other ways to implement AOP on Runtime (:

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      There is, but the main promise of AOP is that you can inject the code during compile time without editing the code almost at all. At that point, it's just fancy decoration and interception patterns. Whether you call it an Aspect doesn't matter.

    • @ArgeKumadan
      @ArgeKumadan Před 2 lety

      @@nickchapsas and I like how u read the comments and respond (:

  • @covid20pro86
    @covid20pro86 Před 2 lety

    good video. The decorator seems good enough for most of case. but i am just too accustomated with decorator provided by DryIoc. haha

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Dryloc? Is that a library? I am not aware of it

  • @johanndirry
    @johanndirry Před 2 lety +1

    If you want a free alternative to PostSharp, take a look at Fody

  • @js6pak
    @js6pak Před 2 lety +3

    Aren't both examples you showed aspect-oriented programming? The first one is just compile time (and like literally the worst example of compile time usage I've seen) and second one is runtime. Also first time I see postsharp but it looks fukin **awful**, just use fody or mono.cecil directly, even source generators would be better for this use case. Also you can use service injection if you did postcompile weaving correctly, aka by using IL you could easily just get the _logger from the class you inject into.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Aspect-orientet programming is very specific about it's terminology and implementation. They are both decoration and interception and aspect-oriented programming manifests as decoration and interception but how it is implemented is what makes it AOP. Basically AOP is guaranteed to do either decoration or interception but decoration and interception aren't always AOP.

  • @user-qi5ue3od5l
    @user-qi5ue3od5l Před 2 lety

    Great info! Thanks!!

  • @orxanhamidov80
    @orxanhamidov80 Před 2 lety

    Amazing video, But in my point of view you can implement logging via help of TypeFilter

  • @Mark-px3rq
    @Mark-px3rq Před 2 lety

    I always struggled to find any really useful applications for AOP. There just aren’t that many cross-cutting concerns that a) require something to run at the start or end of a method, and b) can’t be handled by injecting a service and just calling that. Also that 1 line of code to call a service actually _looks_ like code to the uninitiated.
    The first thing everyone always reaches for with AOP is function entry and exit tracing, which is one of the most awful ways of debugging application execution, and has no place in any modern programming language.

    • @Mark-px3rq
      @Mark-px3rq Před 2 lety

      @@roll-outcommanders6520 Can’t you do most of those things with just logging?
      But, yes, I count it as a failure of an environment if your best bet to diagnose a problem is to trawl through the logs.

    • @Mark-px3rq
      @Mark-px3rq Před 2 lety

      Normally you would have your DI framework inject a logger with some knowledge of where is is being used which gets you most of the way there.
      But honestly it’s a difficult argument for me to swallow that a framework is great for adding function entry/exit tracing when function entry/exit tracing is, in my mind, the most undesirable form of logging.
      If you want tracing, tools exist for that.
      Logging should be at the business logic level, and used sparingly at that.

  • @grudgemudgen5488
    @grudgemudgen5488 Před 2 lety

    You should checkout AspectInjector. No DI, true, but for logging, tracing, and metrics DI isn't needed and can be unit testable. I can share examples if interested.

  • @petronas215
    @petronas215 Před 5 měsíci

    This video is about PostSharp is not a good tool. But not about AOP. When you do interceptors with DI it is still AOP.

  • @sodiboo
    @sodiboo Před 2 lety

    Are you allowed to use copilot to get keys like that? Whether you are or not, if i did that, i wouldn't be talking about it in the video, i'd just not mention it at all and technically not lie about how i got access

    • @nickchapsas
      @nickchapsas  Před 2 lety

      I mean, that's exactly the same as searching GitHub for keys. Ofc you are allows touse copilot like that. Talking about it makes it visible so GitHub can fix it.

    • @sodiboo
      @sodiboo Před 2 lety

      @@nickchapsas Oh yeah, i wasn't really thinking about the training set. "source code from publicly available sources" so for that to be possible through copilot someone leaked their key publicly and that made it into the training data, this key could've been pwned even without copilot distributing it.
      Still though, kinda eeh, yes it's good to mention that it's possible so it can be fixed, but i'm still not convinced github would agree with you that "of course you are allowed to use it like that" since you were actively looking for a key to use, and not just to demonstrate this issue

    • @nickchapsas
      @nickchapsas  Před 2 lety

      It's not against the terms of service

  • @RemX405
    @RemX405 Před 2 lety +2

    In your example here with logging of simple ASP.NET Core calls, why don't you simply use a middleware?
    Of course it's pointless in this specific case. Your second "fixed" example is even worse, as you said, it doesn't scale.
    But that's 1 use case example, in 1 specific backend framework (ASP.NET Core). Saying AOP is pointless over this is silly.
    No hate btw, but I feel like you completely missed the mark here.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Middleware applies to everything implicitly. I wanted to show the control of a filter. The second example doesn't scale indeed, but it can if your core interface is generic and register your decorators by convension with Scrutor. You can also very easily make it scalable by moving it to MediatR Pipeline behaviors that can be generically applied to any pipeline. AOP is absolutely pointless in C# and that's a hill I'll die on. Anyone that uses it doesn't understand what a hack they are using to produce really dodgy code.

  • @f0ssig
    @f0ssig Před 2 lety

    Love the filter.
    Hate the wrapped service.

  • @andreilastochkin5133
    @andreilastochkin5133 Před 2 lety +1

    postsharp is a really cool stuff if you can cook it appropriately. i use it for years

  • @pfarkas80hu
    @pfarkas80hu Před 2 lety

    There is a Visual Studio extension that can be used to generate the key.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Yeah but unfortunately I don’t use visual studio

  • @nicktech2152
    @nicktech2152 Před 2 lety +1

    But, Nick, you actually criticize the PostSharp library, and not the AOP, do you? I mean if somebody implements a Singleton via the static reference it is not the issue with the Singleton pattern itself - it is the issue with the implementation.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      I critisize both. It's a pretentious name, which implies a new programming paradigm, for something that should be and can be way simpler. You don't need an "Aspect", at least in my opinion, in C#. I don't know if that changes if you're doing UI and how the need for INotifypropertychanged affects that, but from the perspective of a back end development, i find it completely pointless. Tooling like MediatR's PipelineBehavior can easy do the exact same thing equally as cleanly.

    • @nicktech2152
      @nicktech2152 Před 2 lety +1

      @@nickchapsas I see your point and I agree that this is nowhere near a new programming paradigm it’s just you kinda criticise the approach by criticising the library which is kind of a dirty trick to me 😅 I mean I doubt AOP denies the usage of Dependency Injection?

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      It does on compile time injection scenarios (Fody is a lot better at this but I still don't like it). There are also runtime scenarios ofc, but at that point, you don't need any of the fluff of AOP. Simply do decorators and interception. Java is better at this with AspectJ.

  • @jcampos
    @jcampos Před 2 lety +1

    While I agree with the general idea (and I use Mediator and pipelines for all my cross-cutting concerns), the title is a bit misleading, because you are showing "non-postsharp" methods of doing actual AOP... except maybe for the final method, doing a proxy service, but on any serious application, you'd end up (if using that method) doing an automatic proxy generator (like Castle or similars), or using source generators... but then you'd actually be doing AOP too :-)
    Aspect-oriented programming does IMO make sense... post-sharp? Well, there are better, and more controllable ways, but that doesn't make it "non-AOP" :-)

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      The thing is that dynamically registering a decorator or an interceptor isn't AOP. Those patterns exist independently. Just using them doesn't mean you are using a whole new programming paradigm. You can do all that without using aspects.

    • @jcampos
      @jcampos Před 2 lety

      @@nickchapsas my understanding of AOP is "separating cross-cutting concerns from the code by specifying them in some way -decorators, conventions, etc-" . Wikipedia (yeah, I know ;-) ) seems to agree with me: en.wikipedia.org/wiki/Aspect-oriented_programming
      For most .NET people though (myself included), AOP typically means only "use postsharp" ;-)
      Having said that, whether it's AOP or not (which could be debatable), just be sure that I agree with your video (like I do on most of them), that there are better ways than postsharp :-)

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      @@jcampos AOP is very specific on how this is achieved through the idea of "the aspect". Wikipedia seems to agree with me. Both AspectJ, which is better than PostSharp IMO and I actually like in Java, and PostSharp, implement that idea too. Decoration and interception are independent patterns. AOP is using them to implement itself. That doesn't make the patterns AOP.

    • @jcampos
      @jcampos Před 2 lety

      @@nickchapsas i get your view, we could debate this, but never over youtube. Come to Spain and I'll pay beers until you give me the reason 😜

    • @nickchapsas
      @nickchapsas  Před 2 lety +2

      @@jcampos Ok you won, AOP is the best thing since sliced bread. Give me the beers!

  • @emreaka3965
    @emreaka3965 Před rokem

    Well We have AOP and We use the way to inject services as you showed. Öhöhöm... Lemme keep watching...

  • @ws_stelzi79
    @ws_stelzi79 Před 2 lety

    Hey getting a Key suggested from someone that checked a valid key into source control is boss level dev hacking cracking skill! 😁😁😎🙄😏

  • @BryonLape
    @BryonLape Před 9 měsíci

    And now you can copy/paste the structure everywhere.

  • @10199able
    @10199able Před 2 lety

    How do you find time to make so many videos with outstanding content?

  • @yuragutyk8028
    @yuragutyk8028 Před 2 lety

    imho, there is no sense to test aspects (logging\metrics\etc). just because an aspect shouldn't contain BL.
    ex for UTs you will inject NullLogger, which is basically NOP. so what's the point to test logging if now it goes as an aspect?
    also, I find integration/E2E tests more useful than UTs in real projects. UTs are only to show good coverage for your manager)

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Unit tests aren't limited to business logic. In fact, unit tests aren't really for business logic as you'd probably test business logic holistically higher. The point of testing logging or metric collection is that you might have a system like Grafana or DataDog, alerting on those text matches from the logs or the metrics. If you suddenly stop logging for some reason you just broke your alerts. It looks to me like people are looking for excuses to not write tests. You should have both unit, integration and e2e tests.

    • @yuragutyk8028
      @yuragutyk8028 Před 2 lety

      ​@@nickchapsas "suddenly stop logging for some reason" this sounds like some infra issue)
      for sure sometimes testing logging/metrics are needed but this doesn't mean that AOP in c# is pointless
      otherwise, postsharp\fody will never be developed and used in production

    • @nickchapsas
      @nickchapsas  Před 2 lety

      @@yuragutyk8028 Sorry I probably didn't phrase that correctly. I mean, suddenly you are not logging the right message. It's as simple as someone changing the log message for something from one piece of text to another. Something as simple as a typo text fix can break an alert. Also, in case that wasn't extremely obvious, there is a "for me" implied on every single video I make. Those aren't objective takes. Everything about topics like these are subjective. Even when I talk about performance and I know objectively faster things, that doesn't mean that you shouldn bother actually doing anything about it because the need for performance itself is subjective. The fact that some people think it is useful and that it's something that works for them isn't invalid the moment a random dude in the internet posts his take.

    • @yuragutyk8028
      @yuragutyk8028 Před 2 lety

      @@nickchapsas don't get me wrong
      kudos to you for sharing interesting stuff on the regular basis
      I'm just trying to say that with 80k+ followers here you are not just a software engineer but an opinion leader as well
      so c# newbies could skip AOP just because this video

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      @@yuragutyk8028 That's a fair point and sometimes I don't think about it. However, I do honestly believe that in the context of back-end .NET/C# newbies should skip AOP. I do not thing that it is a good practice and I think that there are better ways to deal with cross cutting concerns.

  • @fotofoxes2255
    @fotofoxes2255 Před rokem

    You can get dependencies from the HttpContext

  • @anarhistul7257
    @anarhistul7257 Před 2 lety

    Your own website now... aren't we fancy? :)

  • @marsovac
    @marsovac Před rokem

    it is not only violating DRY but also KISS

  • @EverRusting
    @EverRusting Před rokem

    I write this in every applicable video but I think all these problems and other ones could be solved easily if C# gave us a way to write custom syntactic sugar

  • @williamliu8985
    @williamliu8985 Před 2 lety

    How about using the new source code generator?

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Source generators cannot change existing code so I can't see how that woud work

    • @willinton06
      @willinton06 Před 2 lety

      @@nickchapsas you could use a source generator to make the entire class with the try finally, then you just need to inject

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Yeah but that is REALLY complicated because your own code still stays in the project, and then how do you generate the code? Partial classes? Attributes? It's not as easy as it sounds.

    • @willinton06
      @willinton06 Před 2 lety

      @@nickchapsas Partial classes *and* attributes, whatever pleases the dark lord

  • @andreilastochkin5133
    @andreilastochkin5133 Před 2 lety

    in fact, it's not so impossible to supply the interceptor with a DI-ed object:
    public class MyInterceptionAttribute : MethodInterceptionAspect
    {
    public override void OnInvoke(MethodInterceptionArgs args)
    {
    var logger = args.Instance as ILoggerProvider;
    logger?.Log("...");
    args.Proceed();
    logger?.Log("...");
    }
    }
    interface ILoggerProvider
    {
    ILogger Logger { get; }
    }
    //----------------------------------------------
    class MyController : ILoggerProvider
    {
    public ILogger Logger { get; }
    public MyController(ILogger looger)
    {
    Logger = looger;
    }
    [MyInterception]
    public void MyMethod()
    {
    }
    }

    • @nickchapsas
      @nickchapsas  Před 2 lety +3

      This is so horrible. Having the controller implement an interface just to make a very hacky dependency injection work is absolutely terrible. No idea how people think this is acceptable.

    • @andreilastochkin5133
      @andreilastochkin5133 Před 2 lety +1

      ya, sure. while making a wrapper for every class is a pretty good idea ))

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      @@andreilastochkin5133 Firstly, it's absolutely better than what you presented. But the thing is, you don't need to. You can make a generic implementation of it, and dynamically register it on top of your services that need it in DI with dependency redirection. I know you're using PostSharp, but it is a really cheap way to write code that looks smart but really isn't.

  • @ja-rek8846
    @ja-rek8846 Před 9 měsíci

    For me the best is Interceptors from Autofac.

  • @yetanotherdex
    @yetanotherdex Před 2 lety

    code generators could create the decorators based on the interfaces

  • @tarekel-shahawy891
    @tarekel-shahawy891 Před 2 lety

    i just like before even completing the video 😂

  • @clearlyunwell
    @clearlyunwell Před 2 lety

    👍🏽

  • @charles_kuperus
    @charles_kuperus Před 2 lety

    fully solid

  • @JensDll
    @JensDll Před 2 lety

    So it's basically the C#'s way of trying to do decorators

  • @sarabwt
    @sarabwt Před 2 lety

    You are wrong about the testability. If you follow hexagonal, you would have a Interceptor.intercept(method/context), and then make an attribute/annotation out of it. Your attribute would only execute your interceptor. No injection framework magic, just keep a private instance of Interceptor in the Attribute.
    class Attribute : MIA {
    Interceptor i = ...
    OnInvokeAsync(param) {
    i.intercept(param)
    }
    }
    You never test the Attribute class, but always test the interceptor, because Attribute does not contain any complex logic you would have to test.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      And how are the interceptor services instantiated in DI? You example is incomplete.

    • @sarabwt
      @sarabwt Před 2 lety

      @@nickchapsas You wouldn't register the interceptor in a DI framework. You would create a class and pass dependencies into it, without the DI framework, the good old way. You would keep the logging and stuff like that outside of the framework, because you would leave it outside of the framework anyway.
      When you have:
      class Attribute : MIA {
      OnInvokeAsync(param) {
      ...
      }
      }
      you are correct, you cannot test the Attribute. And you cannot use dependencies from DI framework inside the method. You would do something like:
      class Attribute : MIA {
      OnInvokeAsync(param) {
      Logger l = new Logger();
      l.log("start");
      param.invoke();
      l.log("end");
      }
      }
      This is not testable, however this is:
      class Attribute : MIA {
      Interceptor i = new Interceptor(new Logger());
      OnInvokeAsync(param) {
      i.intercept(param)
      }
      }
      You would not test the Attribute class, but the Interceptor.intercept(param) (mock the Logger and param and check if all the calls happen correctly I guess). The Attribute just passes the param, so it doesn't need any testing, all of the logic is inside the intercept method. Is this more clear?
      EDIT: The main idea is to have a testable core. You can hook that core into the "framework" (Attribute in this case) after, but the code should be working without a framework anyways. In this case using an Attribute is a syntactic sugar if that makes sense.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      @@sarabwt Truly ugly stuff. Abstractions should depend on details, which is exactly what you'd violating. Which is exactly my point. You are using a terrible practice and you rationalise it's good because you can test it but you just coupled the attribute with the logger. Just bad and a reinforement to the point of this video.

    • @sarabwt
      @sarabwt Před 2 lety

      @@nickchapsas Amm... What exactly are you on about? Attribute is not an abstraction, it is an interceptor of some sort. It is a "framework" that lets you do some stuff before you invoke a method. And no, it is not a terrible practice, just because Attribute is not hooked into a DI framework. If you don't like it to be bound to the logger, use InterceptorFactory or something... You will decouple the creation of the interceptor from the Attribute. I could also argue that is it horrible to depend on a framework to do everything for you, because you are... well... tightly coupled to the framework...

    • @mAcCoLo666
      @mAcCoLo666 Před 2 lety +2

      @@nickchapsas If your attribute depends on the logger to do its job, then they're coupled anyway. DI framework is just helping you out by taking the pain of typing all those "new..." lines off of you, it's not changing the fact that you need a logger as a param to your attribute.

  • @krzysztofklein3057
    @krzysztofklein3057 Před 2 lety +1

    I think DI should be a feature of the language itself, not just part of a framework.

  • @danrayson
    @danrayson Před rokem +1

    "Can't have dependency injection". Why? Put your dependencies where they should be, in the aspect, NOT the attribute. The Attribute is there for Advice, NOT dependencies.
    Why are you thinking the attribute and the aspect are the same thing?! It looks to me like you think AOP is pointless because you don't know how to do it properly...

  • @hamza.abdullah807
    @hamza.abdullah807 Před 2 lety

    At 14:15 you're basically doing the same thing you have classed as "horrible" with doing DI with post sharp, which is using the Service Locator (anti)pattern, so not sure I like that approach.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      Wrong. This is not service locator. It is dependency redirection on the DI level. Service locator is when you are injective the IServiceProvider directly into a class without knowing exactly what this class will need to resolve. However in that example I am using that interface's contract to do decoration through dependency redirection. They are completely different things.

  • @WillEhrendreich
    @WillEhrendreich Před 2 lety

    For logging honestly I just do an extension method on string, and I just say.. "thing I wanna say".log()

    • @nickchapsas
      @nickchapsas  Před 2 lety +3

      That must be a nightmare to unit test

    • @jackoberto01
      @jackoberto01 Před 2 lety

      That only allows you to have a static implementation of you logging or do you statically get some ILogger from a container in you extension method? Either way that does sound horrible to unit test

    • @WillEhrendreich
      @WillEhrendreich Před 2 lety

      @@nickchapsas I'm not unit testing my logging. I'm not depending upon logic that's in the logger in any sense, im confused about why I'd need to care? Legit why do others unit test logging? I'm building my fist real application by myself here, and I'm the only software developer in my company, so it really is the wild west for me. Why do I care to have a unit test for my logging? I'm only using it to write a log file for a pretty straightforward crud application.. What would I do differently?

    • @WillEhrendreich
      @WillEhrendreich Před 2 lety

      @@jackoberto01 I'm using serilog as my ilogger.. Why would one unit test their logging?

    • @nickchapsas
      @nickchapsas  Před 2 lety

      @@WillEhrendreich Logging and monitoring code is crucial when it comes to alerting. You might want to alert on an error log, when you log an exception, or you might wanna collect a metric based on X amounts of logs that start with, equal or contain text. If you change the text of the log and you don't have unit tests covering it, then it is very unlikely you will remember that this log entry actually has a purpose outside of your application code. Having unit tests covering it ensures you acknowledge it and you are less likely to fall into that trap.

  • @paulalves966
    @paulalves966 Před 2 lety

    🙄

  • @XtroTheArctic
    @XtroTheArctic Před 2 lety +2

    Discouraging AOP just because not being able to use DI in it? Dependency Injection is a curse! It brings much more problems that it promises to solve. OK, I get you are a millennial and it's expected to get weird stuff from your generation but this much... is too much for me. Unsubscribing. Bye.

    • @nickchapsas
      @nickchapsas  Před 2 lety

      Dependency injection isn’t a new concept lol

    • @XtroTheArctic
      @XtroTheArctic Před 2 lety +1

      @@nickchapsas As expected, you only focused on the least important word in my comment "modern". Omitting this word wouldn't change the meaning of my comment one bit. On the contrary, I never said or meant DI is a new concept. Here I'm editing my comment for removing that word and you are still very wrong about AOP and DI. LOL.

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      @@XtroTheArctic That's totally fine. It is ok to be wrong about things. We don't have to agree. I hate AOP, and you seem to like it. That is absolutely and perfectly fine and there is nothing wrong with it. I am not wrong for hating it and you are not wrong for liking it. If you like it then this video shouldn't discourage you from using it or make you feel that you're doing something wrong.

  • @Euquila
    @Euquila Před 2 lety

    What's important to realize is that DRY, OOP, FP, SOLID, TDD and basically any aspect or "practice" in programming is all highly contextual. If you are a single programmer trying to implement some features for a potential client, then just get the fkn thing programmed ASAP. Violate all the principles... just hurry and win the contract.

    • @koderkev42
      @koderkev42 Před 10 měsíci

      You forgot the second part of that. "Tell yourself you will fix it later and after winning the contract, slow your development down to a crawl as you toil over meaningless unit-tests and patterns, etc. Why must we keep over-complicating things? For non-life critical software (most of it), I routinely see dogmatic following of these "principles" result in bloated, hard to maintain code.

  • @darkogele
    @darkogele Před 2 lety

    Hmm maybe you will get more views if u Use vs 2022 i mean people are used to that more compare to rider :P

    • @nickchapsas
      @nickchapsas  Před 2 lety +1

      Yeah but I like to use the IDE that I think is the best for me so

    • @alansinoracki8508
      @alansinoracki8508 Před 2 lety

      Visual studio feels like a toy compared to rider though. In vs 2019 i couldn't even setup my code formating properly and in rider i can easily set up my own formting and code conventions for every language that i use then transfer it using my jetbrains account on every pc that i use. Not to mention seamless db integration, language injection, better refactoring sugestions, better git integration and so on

    • @darkogele
      @darkogele Před 2 lety

      @@alansinoracki8508 Toy working with Winforms, WPF, WCF, Blazzor is not working properly in rider also drag and drop not existis in rider. Working with API in naked c# is amazing but if u need UI not that good. So i wouldent say vs 2022 is toy vs rider.

    • @alansinoracki8508
      @alansinoracki8508 Před 2 lety

      @@darkogele Blazzor is working now and they are still improving support. Whats not working in WCF? As of wpf and winforms it's niche usecase and i can do drag and drop and xaml editing in vs then switch to rider for the rest probably

  • @inittowinit3260
    @inittowinit3260 Před 2 lety

    Like your videos but you talk and go through things way too fast