Why aspect-oriented programming in C# is pointless

Поделиться
HTML-код
  • Опубликовано: 11 дек 2024

Комментарии • 178

  • @nickchapsas
    @nickchapsas  3 года назад +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: ruclips.net/video/2JzQuIvxIqk/видео.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 3 года назад

      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 3 года назад

      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 3 года назад

      @@Karysff My point exactly.

    • @chrisnewey
      @chrisnewey 3 года назад

      MediatR works great for this scenario

    • @Karysff
      @Karysff 3 года назад

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

  • @conway9214
    @conway9214 3 года назад +6

    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  3 года назад +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 3 года назад

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

    • @emreaka3965
      @emreaka3965 2 года назад

      @@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".

  • @ЕвгенийМальцев-ш6в
    @ЕвгенийМальцев-ш6в 3 года назад +5

    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  3 года назад +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 3 года назад +30

    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  3 года назад +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 3 года назад +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.

  • @rsfurlan90
    @rsfurlan90 3 года назад +6

    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 2 года назад

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

  • @evanboltsis
    @evanboltsis 3 года назад +11

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

    • @nickchapsas
      @nickchapsas  3 года назад +8

      You are my new best friend

    • @agsystems8220
      @agsystems8220 3 года назад +2

      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 3 года назад +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.

  • @stephenyork7318
    @stephenyork7318 3 года назад +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.

  • @johanndirry
    @johanndirry 2 года назад +1

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

  • @BrianKapellusch
    @BrianKapellusch 3 года назад +2

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

  • @tofraley
    @tofraley 2 года назад +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.

  • @barmetler
    @barmetler 3 года назад +6

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

  • @seangwright
    @seangwright 2 года назад +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...

  • @peterhevesi6571
    @peterhevesi6571 2 года назад +1

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

  • @sirdondaniel
    @sirdondaniel 2 года назад

    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.

  • @tbddevelops
    @tbddevelops 3 года назад +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  3 года назад +7

      I actually have that video already. Check it out here: ruclips.net/video/2JzQuIvxIqk/видео.html

    • @tbddevelops
      @tbddevelops 3 года назад +1

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

  • @benjaminfortune2707
    @benjaminfortune2707 3 года назад

    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  3 года назад +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.

  • @JochenZeischka
    @JochenZeischka 3 года назад +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  3 года назад +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 3 года назад

      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.

  • @andreilastochkin5133
    @andreilastochkin5133 3 года назад

    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() {}
    }

  • @christianista
    @christianista 2 года назад

    Is it possible to log each method and the parameters ?

  • @kazepis
    @kazepis 2 года назад +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?

  • @JohnZakaria
    @JohnZakaria 3 года назад +2

    Decorator pattern remade?

  • @shurale85
    @shurale85 2 года назад

    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?

  • @apex761
    @apex761 3 года назад +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.

  • @InstrumentalsDaily
    @InstrumentalsDaily 3 года назад +3

    I immensely appreciate your videos Nick

  • @ddanielewski
    @ddanielewski 2 года назад

    How did you get PostSharp to work with Rider?

  • @Jashobantac
    @Jashobantac 3 года назад

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

  • @dksovfen
    @dksovfen 3 года назад +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  3 года назад +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 3 года назад

      @@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  3 года назад +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.

  • @SpaceShot
    @SpaceShot 3 года назад +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 3 года назад +1

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

  • @ristopaasivirta9770
    @ristopaasivirta9770 3 года назад +2

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

  • @LuizHenriqueMiranda
    @LuizHenriqueMiranda 10 месяцев назад

    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.

  • @johnspencer772
    @johnspencer772 3 года назад

    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!!

  • @dmitriyrogovoy
    @dmitriyrogovoy Год назад

    Nick, cant we use MediatR behavior for this instead?

  • @vklooping
    @vklooping 3 года назад

    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  3 года назад +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.

  • @Rajeshsingh-ws5th
    @Rajeshsingh-ws5th 2 года назад

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

  • @ninilab
    @ninilab 3 года назад +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.

  • @Nekroido
    @Nekroido 2 года назад

    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...

  • @ArgeKumadan
    @ArgeKumadan 3 года назад

    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  3 года назад +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 3 года назад

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

  • @danijelzg001
    @danijelzg001 3 года назад

    @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

  • @codewkarim
    @codewkarim 2 года назад

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

  • @Mark-px3rq
    @Mark-px3rq 3 года назад

    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 3 года назад

      @@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 3 года назад

      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.

  • @slyp05
    @slyp05 3 года назад

    What editor are you using?^^

    • @mAcCoLo666
      @mAcCoLo666 3 года назад

      I think it's intellij rider

  • @js6pak
    @js6pak 3 года назад +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  3 года назад

      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.

  • @max-S
    @max-S 3 года назад

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

  • @leonardomoreno23
    @leonardomoreno23 3 года назад

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

    • @nickchapsas
      @nickchapsas  3 года назад +1

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

  • @YariRu
    @YariRu 3 года назад

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

  • @petronas215
    @petronas215 10 месяцев назад

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

  • @andreilastochkin5133
    @andreilastochkin5133 3 года назад

    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  3 года назад +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 3 года назад +1

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

    • @nickchapsas
      @nickchapsas  3 года назад +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.

  • @jcampos
    @jcampos 3 года назад +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  3 года назад +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 3 года назад

      @@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  3 года назад +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 3 года назад

      @@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  3 года назад +2

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

  • @grudgemudgen5488
    @grudgemudgen5488 2 года назад

    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.

  • @RemX405
    @RemX405 3 года назад +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  3 года назад

      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.

  • @marsovac
    @marsovac Год назад

    it is not only violating DRY but also KISS

  • @if07012
    @if07012 3 года назад +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  3 года назад +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 3 года назад +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  3 года назад

      I will have to run benchmarks to get the exact numbers

    • @alansinoracki8508
      @alansinoracki8508 3 года назад

      @@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 3 года назад

      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 3 года назад

    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.

  • @sodiboo
    @sodiboo 3 года назад

    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  3 года назад

      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 3 года назад

      @@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  3 года назад

      It's not against the terms of service

  • @andreasdaxer1168
    @andreasdaxer1168 3 года назад

    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 3 года назад +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.

  • @nicktech2152
    @nicktech2152 3 года назад +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  3 года назад +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 3 года назад +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  3 года назад +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.

  • @f0ssig
    @f0ssig 3 года назад

    Love the filter.
    Hate the wrapped service.

  • @pfarkas80hu
    @pfarkas80hu 3 года назад

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

    • @nickchapsas
      @nickchapsas  3 года назад

      Yeah but unfortunately I don’t use visual studio

  • @andreilastochkin5133
    @andreilastochkin5133 3 года назад +1

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

  • @Alx-gj2uz
    @Alx-gj2uz 3 года назад

    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  3 года назад +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: ruclips.net/video/2JzQuIvxIqk/видео.html

  • @yuragutyk8028
    @yuragutyk8028 3 года назад

    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  3 года назад

      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 3 года назад

      ​@@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  3 года назад

      @@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 3 года назад

      @@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  3 года назад +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.

  • @sarabwt
    @sarabwt 3 года назад

    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  3 года назад

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

    • @sarabwt
      @sarabwt 3 года назад

      @@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  3 года назад

      @@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 3 года назад

      @@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 3 года назад +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.

  • @covid20pro86
    @covid20pro86 3 года назад

    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  3 года назад

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

  • @williamliu8985
    @williamliu8985 3 года назад

    How about using the new source code generator?

    • @nickchapsas
      @nickchapsas  3 года назад

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

    • @willinton06
      @willinton06 3 года назад

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

    • @nickchapsas
      @nickchapsas  3 года назад

      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 3 года назад

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

  • @orxanhamidov80
    @orxanhamidov80 3 года назад

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

  • @noodle-eater
    @noodle-eater 3 года назад

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

  • @BryonLape
    @BryonLape Год назад

    And now you can copy/paste the structure everywhere.

  • @fotofoxes2255
    @fotofoxes2255 2 года назад

    You can get dependencies from the HttpContext

  • @ws_stelzi79
    @ws_stelzi79 3 года назад

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

  • @anarhistul7257
    @anarhistul7257 3 года назад

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

  • @emreaka3965
    @emreaka3965 2 года назад

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

  • @EverRusting
    @EverRusting 2 года назад

    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

  • @ЕвгенийБогданович-ж7э

    Great info! Thanks!!

  • @yetanotherdex
    @yetanotherdex 3 года назад

    code generators could create the decorators based on the interfaces

  • @hamza.abdullah807
    @hamza.abdullah807 3 года назад

    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  3 года назад +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.

  • @ja-rek8846
    @ja-rek8846 Год назад

    For me the best is Interceptors from Autofac.

  • @tarekel-shahawy891
    @tarekel-shahawy891 3 года назад

    i just like before even completing the video 😂

  • @JensDll
    @JensDll 3 года назад

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

  • @krzysztofklein3057
    @krzysztofklein3057 3 года назад +1

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

  • @10199able
    @10199able 3 года назад

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

  • @XtroTheArctic
    @XtroTheArctic 3 года назад +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  3 года назад

      Dependency injection isn’t a new concept lol

    • @XtroTheArctic
      @XtroTheArctic 3 года назад +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  3 года назад +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.

  • @sandromagalli8587
    @sandromagalli8587 2 года назад

    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.

  • @charles_kuperus
    @charles_kuperus 3 года назад

    fully solid

  • @Euquila
    @Euquila 3 года назад

    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 Год назад

      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.

  • @clearlyunwell
    @clearlyunwell 3 года назад

    👍🏽

  • @WillEhrendreich
    @WillEhrendreich 3 года назад

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

    • @nickchapsas
      @nickchapsas  3 года назад +3

      That must be a nightmare to unit test

    • @jackoberto01
      @jackoberto01 3 года назад

      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 3 года назад

      @@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 3 года назад

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

    • @nickchapsas
      @nickchapsas  3 года назад

      @@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.

  • @inittowinit3260
    @inittowinit3260 2 года назад

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

  • @paulalves966
    @paulalves966 3 года назад

    🙄

  • @darkogele
    @darkogele 3 года назад

    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  3 года назад +1

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

    • @alansinoracki8508
      @alansinoracki8508 3 года назад

      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 3 года назад

      @@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 3 года назад

      @@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