Why aspect-oriented programming in C# is pointless

  Рет қаралды 46,898

Nick Chapsas

Nick Chapsas

Күн бұрын

Пікірлер: 177
@nickchapsas
@nickchapsas 3 жыл бұрын
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: kzbin.info/www/bejne/aHvdgqh_q918p80 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 жыл бұрын
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 жыл бұрын
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".
@AB-jt6ic
@AB-jt6ic 3 жыл бұрын
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.
@rsfurlan90
@rsfurlan90 3 жыл бұрын
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...
@ЕвгенийМальцев-ш6в
@ЕвгенийМальцев-ш6в 3 жыл бұрын
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 жыл бұрын
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.
@BrianKapellusch
@BrianKapellusch 3 жыл бұрын
What about AOP interceptors vis an IOC container like structuremap? You can inject via IOC into those aspects I thought
@evanboltsis
@evanboltsis 3 жыл бұрын
The key that Nick did not show was 4202A873-1917-4A20-ABB1-8C4936FE5069
@nickchapsas
@nickchapsas 3 жыл бұрын
You are my new best friend
@agsystems8220
@agsystems8220 3 жыл бұрын
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
@tofraley
@tofraley 2 жыл бұрын
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.
@hernanar3647
@hernanar3647 3 жыл бұрын
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 жыл бұрын
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 жыл бұрын
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.
@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?
@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 жыл бұрын
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 2 жыл бұрын
URGENT: Controller level any attribute runs but what about at domain or data access layer attribute or intercepter? Please discuss.
@stephenyork7318
@stephenyork7318 3 жыл бұрын
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.
@christianista
@christianista 2 жыл бұрын
Is it possible to log each method and the parameters ?
@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 жыл бұрын
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.
@dksovfen
@dksovfen 3 жыл бұрын
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 жыл бұрын
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 жыл бұрын
​@@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.
@ddanielewski
@ddanielewski 2 жыл бұрын
How did you get PostSharp to work with Rider?
@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.
@peterhevesi6571
@peterhevesi6571 2 жыл бұрын
There is now a clean rewrite of PostSharp called Metalama, which does supports Dependancy Injection
@dmitriyrogovoy
@dmitriyrogovoy Жыл бұрын
Nick, cant we use MediatR behavior for this instead?
@kazepis
@kazepis 2 жыл бұрын
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?
@LuizHenriqueMiranda
@LuizHenriqueMiranda 11 ай бұрын
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.
@JochenZeischka
@JochenZeischka 3 жыл бұрын
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 жыл бұрын
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.
@tbddevelops
@tbddevelops 3 жыл бұрын
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 жыл бұрын
I actually have that video already. Check it out here: kzbin.info/www/bejne/aHvdgqh_q918p80
@tbddevelops
@tbddevelops 3 жыл бұрын
@@nickchapsas Thank you. That was very helpful. I've only recently come across your videos, I'm catching up.
@JohnZakaria
@JohnZakaria 3 жыл бұрын
Decorator pattern remade?
@johanndirry
@johanndirry 2 жыл бұрын
If you want a free alternative to PostSharp, take a look at Fody
@seangwright
@seangwright 3 жыл бұрын
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...
@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
@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
@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 жыл бұрын
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 (:
@barmetler
@barmetler 3 жыл бұрын
Using copilot to get keys for this stuff is so funny, I can't wait to be accepted into the program :(
@Jashobantac
@Jashobantac 3 жыл бұрын
Thanks Nick for adding something on AOP. I had been requesting something on AOP for quite some time.
@petronas215
@petronas215 11 ай бұрын
This video is about PostSharp is not a good tool. But not about AOP. When you do interceptors with DI it is still AOP.
@Nekroido
@Nekroido 3 жыл бұрын
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...
@slyp05
@slyp05 3 жыл бұрын
What editor are you using?^^
@mAcCoLo666
@mAcCoLo666 3 жыл бұрын
I think it's intellij rider
@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.
@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!!
@codewkarim
@codewkarim 3 жыл бұрын
I think it's also possible to achieve some scenarios using Mediator since it creates its own pipeline (but again it's a package).
@SpaceShot
@SpaceShot 3 жыл бұрын
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 жыл бұрын
This, as you called “technique”, is nothing new. It is classical decorator pattern from GoF book.
@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() {} }
@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 жыл бұрын
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.
@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 жыл бұрын
@@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.
@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)
@js6pak
@js6pak 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.
@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 жыл бұрын
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 жыл бұрын
ya, sure. while making a wrapper for every class is a pretty good idea ))
@nickchapsas
@nickchapsas 3 жыл бұрын
@@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.
@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 жыл бұрын
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: kzbin.info/www/bejne/aHvdgqh_q918p80
@apex761
@apex761 3 жыл бұрын
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.
@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 жыл бұрын
For the built in one you can use Scrutor which also add similar decoration behaviour very easily
@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 жыл бұрын
@@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.
@if07012
@if07012 3 жыл бұрын
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 жыл бұрын
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 жыл бұрын
@@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
@InstrumentalsDaily
@InstrumentalsDaily 3 жыл бұрын
I immensely appreciate your videos Nick
@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
@nicktech2152
@nicktech2152 3 жыл бұрын
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 жыл бұрын
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 жыл бұрын
@@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 жыл бұрын
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.
@ristopaasivirta9770
@ristopaasivirta9770 3 жыл бұрын
License key for thousand lines of code? Fine... _proceeds to remove all line breaks_
@ninilab
@ninilab 3 жыл бұрын
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.
@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.
@RemX405
@RemX405 3 жыл бұрын
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.
@jcampos
@jcampos 3 жыл бұрын
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 жыл бұрын
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 жыл бұрын
@@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 жыл бұрын
@@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.
@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
@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
@YariRu
@YariRu 3 жыл бұрын
Now I get strong flashbacks from 2008 about PostSharp. Used that for logging and transaction context
@fotofoxes2255
@fotofoxes2255 2 жыл бұрын
You can get dependencies from the HttpContext
@yetanotherdex
@yetanotherdex 3 жыл бұрын
code generators could create the decorators based on the interfaces
@BryonLape
@BryonLape Жыл бұрын
And now you can copy/paste the structure everywhere.
@noodle-eater
@noodle-eater 3 жыл бұрын
Cool, a few month ago i was searching and interesting with this topic but not dig deeper yet
@f0ssig
@f0ssig 3 жыл бұрын
Love the filter. Hate the wrapped service.
@andreilastochkin5133
@andreilastochkin5133 3 жыл бұрын
postsharp is a really cool stuff if you can cook it appropriately. i use it for years
@orxanhamidov80
@orxanhamidov80 3 жыл бұрын
Amazing video, But in my point of view you can implement logging via help of TypeFilter
@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 жыл бұрын
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.
@marsovac
@marsovac Жыл бұрын
it is not only violating DRY but also KISS
@anarhistul7257
@anarhistul7257 3 жыл бұрын
Your own website now... aren't we fancy? :)
@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
@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! 😁😁😎🙄😏
@10199able
@10199able 3 жыл бұрын
How do you find time to make so many videos with outstanding content?
@ja-rek8846
@ja-rek8846 Жыл бұрын
For me the best is Interceptors from Autofac.
@emreaka3965
@emreaka3965 2 жыл бұрын
Well We have AOP and We use the way to inject services as you showed. Öhöhöm... Lemme keep watching...
@ЕвгенийБогданович-ж7э
@ЕвгенийБогданович-ж7э 3 жыл бұрын
Great info! Thanks!!
@JensDll
@JensDll 3 жыл бұрын
So it's basically the C#'s way of trying to do decorators
@krzysztofklein3057
@krzysztofklein3057 3 жыл бұрын
I think DI should be a feature of the language itself, not just part of a framework.
@XtroTheArctic
@XtroTheArctic 3 жыл бұрын
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 жыл бұрын
@@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 жыл бұрын
@@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.
@charles_kuperus
@charles_kuperus 3 жыл бұрын
fully solid
@tarekel-shahawy891
@tarekel-shahawy891 3 жыл бұрын
i just like before even completing the video 😂
@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 жыл бұрын
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.
@paulalves966
@paulalves966 3 жыл бұрын
🙄
@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.
@inittowinit3260
@inittowinit3260 2 жыл бұрын
Like your videos but you talk and go through things way too fast
@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 жыл бұрын
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
Metaprogramming and JIT Compilers - A Brief Overview
15:59
VoxelRifts
Рет қаралды 30 М.
黑天使只对C罗有感觉#short #angel #clown
00:39
Super Beauty team
Рет қаралды 36 МЛН
Что-что Мурсдей говорит? 💭 #симбочка #симба #мурсдей
00:19
Are events in C# even relevant anymore?
16:19
Nick Chapsas
Рет қаралды 172 М.
Don't throw exceptions in C#. Do this instead
18:13
Nick Chapsas
Рет қаралды 264 М.
Object Oriented Programming is Good | Prime Reacts
31:30
ThePrimeTime
Рет қаралды 331 М.
Why I Prefer Exceptions To Errors
1:05:31
ThePrimeTime
Рет қаралды 195 М.
What is Span in C# and why you should be using it
15:15
Nick Chapsas
Рет қаралды 262 М.
Functional Programming IS NO BETTER than Object Oriented Programming
23:01
Continuous Delivery
Рет қаралды 49 М.
You are doing .NET logging wrong. Let's fix it
25:29
Nick Chapsas
Рет қаралды 175 М.
Writing C# without allocating ANY memory
19:36
Nick Chapsas
Рет қаралды 153 М.
Don't Use This LINQ Feature. It's Bad. | Code Cop #026
8:37
Nick Chapsas
Рет қаралды 11 М.
黑天使只对C罗有感觉#short #angel #clown
00:39
Super Beauty team
Рет қаралды 36 МЛН