I was thinking to comment the same thing until Nick showed how to pass the action to the options configuration instead of the manual call. Soooo close!
@@nickchapsas Even if I knew about this, I still learnt about the IOptions and .Configure(options). I definitely learnt something anyways. Always love to improve even these small details
Great video! I'm trying to level up my software engineering skills, and your videos on Dependency Injection are great! The MS documentation often has a real "A monad is just a monoid in the category of endofunctors" energy, which frustrates me to no end. Your videos really help fill in the gaps. I see some folks complaining this video was too simple but it was perfect for me! Thank you!
I was just about to stop the video, and go look at the framework source code to see how it was implemented, and then you said, "...but I want you to know what happens behind the scenes..." Thank you, sir! Love your videos man. I'm a big believer in pair programming. If for no other reason, than because you pick up little bits and pieces... tricks and tips... from every developer you work with. And the more you work with them, the more of their tricks you learn. Those things can really improve a developer's productivity more than just about anything else, I think. At least when you're kind of at the more advanced level. Watching your videos is like pair programming with a really talented developer who's way more up on the latest stuff than I am. Beyond the intended content, which is always excellent and I learn a bunch from every time, I just get so much from how you do things. IDE stuff, shortcuts I didn't know existed, extensions that do really cool stuff, etc. You and Jon Skeet should get together and do a C# "Master of Masters" Class. lol.
I use the Action/Func delegates effectively when dealing with complex and/or disposable objects. I set up the disposable object with default values or parameter-fed ones. Then the last param would be the Action or Func which contains the substantiated object allowing for a much simpler mutation method. It's really designed for other devs on a project to use a thing without fully understanding it. It allows for polymorphism, passing in any version of type; such as a semi-configured type that doesn't need reconfiguring. Overengineering at its finest.
That's exactly how I found out too! I was like "How the hell does that work??". And then i see the code and I was like "OK but how the hell does that work?" 😂
Pretty gnarly what that Action.Invoke(arg) method is doing to the arg. It wouldn't be obvious normally that it's injecting it own internal version of "arg" into the original arg you used to invoke it! You'd think that invoking a delegate with some argument, would actually use THAT argument, not replace it with something else before using it.
Ah, so it’s a post-constructor modify the contents of the object. The method that receives this action initialises everything, but this parameter lets you tweak anything about that object - but they might create YYY and only let you access an interface or base class with designated customization fields.
Thank you, abj! I was trying to figure out the primary purpose of this approach and your first sentence explained it perfectly! Another tool for the toolbox.
Thank you! I had no idea you could just hand the action to the .Configure method to inject the IOptions. The whole time I had been manually registering a factory that uses the action, but this is much cleaner.
Hi Nick. Just wanted to say that I really like your courses so far (well, I am still busy with the Minimal API course but it is great so far). I was afraid that it would go too fast as your videos tend to assume a certain level of knowledge and you go over certain parts really fast or skip them all together (which is just me and my lack of certain core concepts that trips me up) but your courses are totally different. You really explain things from the basics to the advanced level and go through them slowly and in a structured manner. Your knowledge of the subject really shows. Thank you so much for making me a better developer
Thanks Dennis I really appreciate your kind words. For courses I take it slow because I don't need to please the RUclips algorithm so I can cover more things, in depth and at a slower pace. Glad you like it!
Invoking is a great way to create an initialised Options object. However this object can be different to the one received from DI. Invoking the action will not call any PostConfigure/IPostConfigureOptions registered. This can lead to issues where some validation will not fail or some values wont get set.
It's much more useful that just configuration. Func/Action is like a lambda abstraction that can replace the interfaces. It's more generalised than an interface as you just need to satisfy the lambda parameters not the interface names.
One part of this options extension convention that is missing is to return IServicesCollection from your extension method, which allows other extensions to continue to be chained. It's always annoying when libraries have a void return for IServicesCollection extensions. Why do they want to break my flow!? 😀
I was thinking to myself "This isn't a trick, this is just how you're supposed to use the framework," but then I noticed that he put "trick" in scare quotes, so essentially, the video title is "The setup non-trick that [...]" which is entirely accurate! 😂
As much as I love your videos, and as much as they make me feel like an amateur sometimes, this one was sadly inaccurate (EDIT: not inaccurate, only slightly misleading, see my other comment below). First, the "behind the scenes" part does not show how powerful this mechanism actually is: the options are lazily initialized on first access to the IOptions.Value property. Second, talking about IOptions without mentioning IConfigureOptions is a missed opportunity: with the latter, you can use objects from the DI container (like IConfiguration) for setting your options. This is so insanely powerful. Thanks for all your content, it is so incredibly valuable to the experienced .NET developer.
Sorry but I think you need to rephrase your comment. When the content is missing something that you think should be included, it isn’t inaccurate unless the content itself is wrong. Firstly, The video doesn’t focus on the configuration itself but rather the Action mechanism. The configuration is just a mean to demonstrate it and show the registration part. Going in depth about how IOptions works and also mentioning ConfigureOptions would be offtopic for this video. Both topics I have covered in an appropriate video. If I wanted to be even more accurate on your first point I would also mention that IOptions.Value is cached on first load in the OptionsCache object. That’s in scope for a different video but please understand that not mentioning everything about a topic doesn’t make the existing content inaccurate.
Fair enough. The "inaccurate" part was about you saying that "behind the scenes" the options are initialized inside the Add method, which is not what happens when calling Configure. Sorry if my original comment was unclear. Keep up the good work!
@@nickchapsas Yes, Thomas is right on that. Nick, with your explanation, I though that your behind the scene was an equivalent. However, with Invoke(), the options are configured when you call the Add... method. With Configure(), it's called only if there is a need for the options later in the code, for instance when you injected it in your controller.
@@NicolasCadilhac Which is what I mentioned in the annotation in the video at 8:35. That, to have the same experience you need to register it using the Options.Create(myAwesomeOption) so you actually register the options object.
Hi Nick, honnestly i really like the content of your videos as c# is really the language i am working as a day to day. For this video i understand that it work to use an action instead of creating an object but what i dont really know is why not using an object. Usualy i like adding complexity or more layer when it provides feature or prevent issues but here i dont get it. I can inderstand the lazy loading strategy but that's all. Thanks for your help.
Simple and easy explanation and demo as always, great work! I like to use this approach as well, but usually I struggle to use this together with IConfiguration. I have the feeling I miss something to make it elegant. Can you follow up with a more extended example and the usage of IConfigration?
It is easy as that: SomeExtensionMethod(options => configuration.Bind(options)); This uses the binding feature of IConfiguration which maps key / value pairs provided by the registered IConfigurationProvider implementations to the options instance passed to the Action callback.
I really appreciate your videos about open source libraries, one thing is always bugging me! We all take for granted dotnet swagger availability out of the box, but we don't know about open source library behind it. I didn't even know it's name for long time. Please talk about Swashbuckle.
I am probably missing something but I really don't get the point here. It's obviously A way of doing it and its nice to know how it works under the hood but I think just seeing the lambda parameter is fairly self-explanatory in how this might be implemented under the hood. My main question, which this video seems to have completely missed is what exactly are the advantages of doing this? The only thing I can come up with of why this is a useful pattern is if your configuration class has some complex setters or properties that can not be set through an object initializer especially in older versions of C#. Is this about using one unified approach everywhere for configuration passing even if its just unnecessary shenanigans in many straight-forward cases?
Thanks for this! Some libraries allows you to leave out the configuration entirely in the "add..." method, but yet if the app settings.json contains a valid section with the options it programagically discover it and configures accordingly. I have tried to do it, but am failing because I dont see how to prevent building the service collection or prevent the service locator antipattern. I would love to see a video on this if what I describe makes any sense (and you know how). Sadly I cant remember what package I saw this behaviour in ATM. You are great !!
So I must be doing something wrong, since when I use the code where it is creating a new instance, the changes are never sent through to the controller's constructor.
Delegates are working, even quite efficiently, why change ? Nice syntax on it it sure, it'll take a bit to learn but sure. They're hard to implement in the first place (properly) This just makes it more obscure.
Hey Nick, do you think this is a better approach to a model binded configuration? I use a lot of configurations that are bound to a model for an easier to read code.
Thank you for this ... One query not related to this topic .. I m using extension method to vertical slice architecture .. is it good to use extension for this n what performance impact ?
Another trick is by using the same Microsoft.Extensions.DependencyInjection name space, which reduces the number of usings in Startup (or Program.cs if you are on .net 6)
Is there a reason why we should avoid adding using statements by using a MS namespace? Almost every library I see uses their own namespace for the DI configuration
@@notmeprobably_ it's just Microsoft's recommendation for extension methods. You can use the namespace of the targeted type that you want to extend. Or you can keep your project's namespace to not get name conflicts
It is part of it too. You only implement or override what you need to and then the action might have some defaults set for you, that you dont need to know about
I did not know about the .Configure and what it actually does (shame on me) but I would literally have done the same thing if I had to implement this with the Action?...
Hi Nick. I just purchased 3 of your courses and I'm very excited to start with them. Any idea why they don't work with Firefox (latest version on Apple M1) but do work in Chrome?
What if your option values come from config (appsettings), would you just set the values in the options lambda in program.cs e.g. options.Something = ConfigurationSection[“Something”]
How does your IDE know that the implementation of this Action should be in the form of 'option => { ... }'? Where does this 'option' argument name come from?
Because you don't have to instantiate any object, you don't need to know the type of the object, you don't need to have long names inline. It's just a better experience for me
In addition, this approach is not just limited to options. You can use it in many places. We have thousands of tests which require us to build an object and send it to some api call. Instead of manually craeting object in each tests and setting various property defaults, while only changing one or two properties. We use this approach to create an object and you can just pass an action to override required properties. The end result is really clean and SRP based code
This approach means you're working with an already constructed options object (Thanks, abj!). So, while configuring your options, you can see what your current options are. Further, the caller doesn't need to merge options changes from each caller into the caller's internal options class: They can merely have an Action that modifies the options object directly (possibly via interface or other encapsulation method).
It took me a while to understand this as well, but the real value here is that when you have a method that takes an object you call it and that is it. If you take a delegate, you can run more than one. It can be configured from json, and this delegate, and a "standard" setup (like something that configures certain settings required for security). One great use I've found for this is json serialization settings. They are big, complicated, and have lots of things that can be modified. You might have one configure method which adds a JsonConverter, specific to the project, one which sets some security based settings (like TypeHandling), and one that sets standard settings across several projects. Certainly not the only way you could handle this, but it's a nice composable approach.
@@nickchapsas Seems like there was a hickup on RUclips's side since yesterday all other videos worked with audio but this one didn't. Now it works fine!
Damn, for the first time watching your videos I couldn’t believe you’re explaining something so simple for so long. I actually thought I’m missing something, so I watched till the end, lol. Keep up the great work, but try not to slide too much into basics, please, because I believe many people came here for advanced stuff, which you cover the best
Fair fair. I wanted to try something more basic because some times people criticise that my videos are too advanced but it’s clear that most people like the advanced stuff so I’ll stick to that
@@nickchapsas Imo those "basic" things are sometimes a great reminder of what is possible and your style of presenting it makes it a great watch everytime. I especially love your videos that do not depend on third party services like AWS because it always carries a healthy dose of "I can try that out immediately" which is insanely valuable to me everytime
@@nickchapsas I have no problem re-learning basic things if they're explained well because there will always be little things I misunderstand or reasonings behind things I wasn't aware of that continue to broaden my understanding of something, I never assume I know everything there is to know about something. Even the simple things have potential to teach you great things. In this case it was just a reminder to simplify my configuration and look into buying your courses finally : )
You have the idea but your implementation is not entirely correct. The first issue is a question of convention. You should return the service collection so you can chain the calls. The second issue is that Configure is guarded against a null configureOptions parameter, therefore calling AddAwesomeness() will throw ArgumentNullException. This is unfortunate because I would rather be able to implement this pattern the way you did, rather than the more verbose implementation we may find in the framework (for instance, look for src/Middleware/Session/src/SessionServiceCollectionExtensions.cs in the repo /dotnet/aspnetcore). The very sad part is that the underlying implementation of the constructed ConfigureNamedOptions accepts a null action (Look for /src/libraries/Microsoft.Extensions.Options/src/ConfigureNamedOptions.cs#L19 in the repo /dotnet/runtime) but the null check was added by the extension method (Look for /src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs#L58).
This video isn't focusing on the registration convension but on the Action part. Yes in a real scenario would also return IServiceCollection. Regarding the null check, I am totally ok with extension methods overriding the underlying behavior if the ultimate goal is to guide the user to a specific implementation
Oh yeah so much money from ads. I don't know where to put it all. For real though, the video doesn't need to be 10 minutes any more to get mid-rolls. 8 minutes is enough. I don't wanna rush any topic that I think needs context. If you want 2 minute video you should check out Fireship
i'm not convinced about these extensions methods and actions for configuration, to me it seems too much of a logic error, something that should be solved in another way; relying on extensions method make it impossible to use a framework without reading the docs a lot, because you would never see those methods if you don't already have all the packages and usings already configured; configuring with an action to me means that separation between configuration and instantiation/logic is not well done, if you were not able to configure everything you needed before constructing the objects; i'm not saying this is heresy, i can understand not everything being perfect, but relying so much on this makes me think something is off
In fact it's the opposite. Remember that just the configuration isn't the only thing you register. In fact it is the minor thing. The main thing is to register the dependencies the way they are intented to be registered. Here is an example. Do you register your MediatR pipelines, and handlers manually? Ofc you don't, that would be silly. Instead you use the configuration extension methods because Jimmy knows better than you and he wants to make it easy for you. That's what it is about. Geting into a package should eb easy. Reading the docs should be something that you might need to do if you need something very specific. Not the general usecase. github.com/jbogard/MediatR/wiki#aspnet-core-or-net-core-in-general
ok configuration was kind of an unlucky word, since we're talking about essentially the architecture of the application; following the MediatR example: would i find Jimmy's helpful method inside the newly instantiated class? nope, it's an extension, it's a separate package (and rightly so); would it be unfathomable to find it and use it? again: no, instead i would say it's a pretty common practice at this point; if we take a more complex object, insetad, for example asp net itself, that thing uses a lot of extensions for adding features: controllers, cors, mvc, etc, but those are pretty big boys, i am not sure i like unleashing objects with that power in this way, i find it's kind of an abuse; a huge single configuration object would not solve this issue either, but at least it's searcheble, it's well defined
"everyone do it therefore it must be right" is not the way i'd like to deal with it; i know it's widespread, i use this too because how could i do otherwise, i see pros and cons, i want to get deeper in the whys and hows
Im currently using this to have each dll grab its correct configuration from a like-minded config tree entry + inject the connectionstring name in each of these so these can then be used in services (which use non orm transactions where connectionstring is needed) without needing to pass a global Configuration to these services where settingspath is the name of the executing assembly where . is replaced with : since ... passing values to this would mean that the caller would need to know the location of the setting but i want the component itself to know the location of the setting not the caller . The IConfiguration configuration then reads any configuration source e.g. jsons it can find. public static readonly string AssemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? "X.Y.Z"; private static readonly string SettingsPath = AssemblyName.Replace('.', ':'); public static IServiceCollection AddXXX(this IServiceCollection services, IConfiguration configuration) { MyAwesomeoption _options = new MyAwesomeoption(); IConfigurationSection configSection = configuration.GetSection(SettingsPath); configSection.Bind(_options); string connectionString = configuration.GetConnectionString(_options.ConnectionName); configuration[SettingsPath + ":ConnectionString"] = connectionString; services .Configure(configuration.GetConnectionString(configuration.GetSection(SettingsPath)) .AddDbContextPool(options => { options.UseSqlServer(configuration.GetConnectionString(_options.ConnectionName}); return services; }
Finally!!!! He covered something I already knew lol 😆. But still a great video as always!
I was thinking to comment the same thing until Nick showed how to pass the action to the options configuration instead of the manual call. Soooo close!
Damn, back to the drawing board 😂
@@nickchapsas no no. This just means I’m ready for Google or Amazon righttt?? 🤣
@@nickchapsas Even if I knew about this, I still learnt about the IOptions and .Configure(options). I definitely learnt something anyways. Always love to improve even these small details
Great video! I'm trying to level up my software engineering skills, and your videos on Dependency Injection are great! The MS documentation often has a real "A monad is just a monoid in the category of endofunctors" energy, which frustrates me to no end. Your videos really help fill in the gaps. I see some folks complaining this video was too simple but it was perfect for me! Thank you!
I was just about to stop the video, and go look at the framework source code to see how it was implemented, and then you said, "...but I want you to know what happens behind the scenes..."
Thank you, sir!
Love your videos man. I'm a big believer in pair programming. If for no other reason, than because you pick up little bits and pieces... tricks and tips... from every developer you work with. And the more you work with them, the more of their tricks you learn. Those things can really improve a developer's productivity more than just about anything else, I think. At least when you're kind of at the more advanced level. Watching your videos is like pair programming with a really talented developer who's way more up on the latest stuff than I am. Beyond the intended content, which is always excellent and I learn a bunch from every time, I just get so much from how you do things. IDE stuff, shortcuts I didn't know existed, extensions that do really cool stuff, etc.
You and Jon Skeet should get together and do a C# "Master of Masters" Class. lol.
I use the Action/Func delegates effectively when dealing with complex and/or disposable objects.
I set up the disposable object with default values or parameter-fed ones. Then the last param would be the Action or Func which contains the substantiated object allowing for a much simpler mutation method.
It's really designed for other devs on a project to use a thing without fully understanding it. It allows for polymorphism, passing in any version of type; such as a semi-configured type that doesn't need reconfiguring.
Overengineering at its finest.
Thanks for this explanation; I didn't really understand the use case from the video
I use that approach in combination with IConfiguration binding . Very powerful stuff.
i love this "pattern" and remember learning how to use it with my own libraries after learning how to use Entity Framework. great video!
Thanks for this, it's something I've been wondering about but never really took the time to investigate.
That's exactly how I found out too! I was like "How the hell does that work??". And then i see the code and I was like "OK but how the hell does that work?" 😂
Pretty gnarly what that Action.Invoke(arg) method is doing to the arg. It wouldn't be obvious normally that it's injecting it own internal version of "arg" into the original arg you used to invoke it! You'd think that invoking a delegate with some argument, would actually use THAT argument, not replace it with something else before using it.
Ah, so it’s a post-constructor modify the contents of the object. The method that receives this action initialises everything, but this parameter lets you tweak anything about that object - but they might create YYY and only let you access an interface or base class with designated customization fields.
That's correct
Thank you, abj! I was trying to figure out the primary purpose of this approach and your first sentence explained it perfectly! Another tool for the toolbox.
Wow thanks for this comment abj, I wasn't understanding it fully until you said that and then it all clicked
Thank you! I had no idea you could just hand the action to the .Configure method to inject the IOptions. The whole time I had been manually registering a factory that uses the action, but this is much cleaner.
Hi Nick. Just wanted to say that I really like your courses so far (well, I am still busy with the Minimal API course but it is great so far). I was afraid that it would go too fast as your videos tend to assume a certain level of knowledge and you go over certain parts really fast or skip them all together (which is just me and my lack of certain core concepts that trips me up) but your courses are totally different. You really explain things from the basics to the advanced level and go through them slowly and in a structured manner. Your knowledge of the subject really shows. Thank you so much for making me a better developer
Thanks Dennis I really appreciate your kind words. For courses I take it slow because I don't need to please the RUclips algorithm so I can cover more things, in depth and at a slower pace. Glad you like it!
I use this to setup data objects for my tests, works like a charm!
Invoking is a great way to create an initialised Options object. However this object can be different to the one received from DI. Invoking the action will not call any PostConfigure/IPostConfigureOptions registered. This can lead to issues where some validation will not fail or some values wont get set.
Great I love it this approach too. But we have to take care about default values or do some validation when something is missing.
It's much more useful that just configuration. Func/Action is like a lambda abstraction that can replace the interfaces. It's more generalised than an interface as you just need to satisfy the lambda parameters not the interface names.
Very cool, I started creating a library that needs to do something like this. Very nice explanation 👍
One part of this options extension convention that is missing is to return IServicesCollection from your extension method, which allows other extensions to continue to be chained. It's always annoying when libraries have a void return for IServicesCollection extensions. Why do they want to break my flow!? 😀
I was thinking to myself "This isn't a trick, this is just how you're supposed to use the framework," but then I noticed that he put "trick" in scare quotes, so essentially, the video title is "The setup non-trick that [...]" which is entirely accurate! 😂
Thank you Nick, awesome as always
As much as I love your videos, and as much as they make me feel like an amateur sometimes, this one was sadly inaccurate (EDIT: not inaccurate, only slightly misleading, see my other comment below).
First, the "behind the scenes" part does not show how powerful this mechanism actually is: the options are lazily initialized on first access to the IOptions.Value property.
Second, talking about IOptions without mentioning IConfigureOptions is a missed opportunity: with the latter, you can use objects from the DI container (like IConfiguration) for setting your options. This is so insanely powerful.
Thanks for all your content, it is so incredibly valuable to the experienced .NET developer.
He already made a video about IOptions: ruclips.net/video/J0EVd5HbtUY/видео.html
Sorry but I think you need to rephrase your comment. When the content is missing something that you think should be included, it isn’t inaccurate unless the content itself is wrong. Firstly, The video doesn’t focus on the configuration itself but rather the Action mechanism. The configuration is just a mean to demonstrate it and show the registration part. Going in depth about how IOptions works and also mentioning ConfigureOptions would be offtopic for this video. Both topics I have covered in an appropriate video. If I wanted to be even more accurate on your first point I would also mention that IOptions.Value is cached on first load in the OptionsCache object. That’s in scope for a different video but please understand that not mentioning everything about a topic doesn’t make the existing content inaccurate.
Fair enough.
The "inaccurate" part was about you saying that "behind the scenes" the options are initialized inside the Add method, which is not what happens when calling Configure.
Sorry if my original comment was unclear. Keep up the good work!
@@nickchapsas Yes, Thomas is right on that. Nick, with your explanation, I though that your behind the scene was an equivalent. However, with Invoke(), the options are configured when you call the Add... method. With Configure(), it's called only if there is a need for the options later in the code, for instance when you injected it in your controller.
@@NicolasCadilhac Which is what I mentioned in the annotation in the video at 8:35. That, to have the same experience you need to register it using the Options.Create(myAwesomeOption) so you actually register the options object.
Hi Nick, honnestly i really like the content of your videos as c# is really the language i am working as a day to day. For this video i understand that it work to use an action instead of creating an object but what i dont really know is why not using an object. Usualy i like adding complexity or more layer when it provides feature or prevent issues but here i dont get it. I can inderstand the lazy loading strategy but that's all. Thanks for your help.
Good thing to keep the code cleaner
Course prices are more accessible now, I think I might actually get both testing courses!
Thanks a lot! Love this approach!
Great explanation! Thank you.
Awesomeness over 9000
Simple and easy explanation and demo as always, great work!
I like to use this approach as well, but usually I struggle to use this together with IConfiguration. I have the feeling I miss something to make it elegant. Can you follow up with a more extended example and the usage of IConfigration?
It is easy as that: SomeExtensionMethod(options => configuration.Bind(options));
This uses the binding feature of IConfiguration which maps key / value pairs provided by the registered IConfigurationProvider implementations to the options instance passed to the Action callback.
Nice video,got to know about cool stuff. So, would it even bind options from configuration object and values in appsettings.json file?
Good video. Thanks again Nick!
I love how you say completely random number and then input 69.. like you don't do that every time you need some arbitrary number.
I was so confused... I thought "completely random number" is always 42!
@@megadesty he likes both.. 69 and 420. Anytime he needs some arbitrary number he uses these
He definitely smirked at 5:48 😏
I really appreciate your videos about open source libraries, one thing is always bugging me!
We all take for granted dotnet swagger availability out of the box, but we don't know about open source library behind it. I didn't even know it's name for long time.
Please talk about Swashbuckle.
You should look at contract testing which can allow you to skip integration testing, shifting you left and introducing a faster feedback loop
Awesome! Elegant! Easy!
I think this pattern is best when using dependency injection.
Great video, keep on the good work! 😇
I am probably missing something but I really don't get the point here.
It's obviously A way of doing it and its nice to know how it works under the hood but I think just seeing the lambda parameter is fairly self-explanatory in how this might be implemented under the hood.
My main question, which this video seems to have completely missed is what exactly are the advantages of doing this?
The only thing I can come up with of why this is a useful pattern is if your configuration class has some complex setters or properties that can not be set through an object initializer especially in older versions of C#.
Is this about using one unified approach everywhere for configuration passing even if its just unnecessary shenanigans in many straight-forward cases?
Thanks for this!
Some libraries allows you to leave out the configuration entirely in the "add..." method, but yet if the app settings.json contains a valid section with the options it programagically discover it and configures accordingly. I have tried to do it, but am failing because I dont see how to prevent building the service collection or prevent the service locator antipattern. I would love to see a video on this if what I describe makes any sense (and you know how). Sadly I cant remember what package I saw this behaviour in ATM.
You are great !!
Nice Age filter
So I must be doing something wrong, since when I use the code where it is creating a new instance, the changes are never sent through to the controller's constructor.
Delegates are working, even quite efficiently, why change ? Nice syntax on it it sure, it'll take a bit to learn but sure.
They're hard to implement in the first place (properly) This just makes it more obscure.
Hey Nick, do you think this is a better approach to a model binded configuration?
I use a lot of configurations that are bound to a model for an easier to read code.
Oh yeah for sure, for the things you own, model binding from the config is fine as long as you also have model explicit overloads
Thank you for this ...
One query not related to this topic ..
I m using extension method to vertical slice architecture .. is it good to use extension for this n what performance impact ?
Another trick is by using the same Microsoft.Extensions.DependencyInjection name space, which reduces the number of usings in Startup (or Program.cs if you are on .net 6)
Is there a reason why we should avoid adding using statements by using a MS namespace? Almost every library I see uses their own namespace for the DI configuration
@@notmeprobably_ it's just Microsoft's recommendation for extension methods. You can use the namespace of the targeted type that you want to extend. Or you can keep your project's namespace to not get name conflicts
Is the act of passing an Action instead of T is so that you can have logic inside your action before settings some properties?
It is part of it too. You only implement or override what you need to and then the action might have some defaults set for you, that you dont need to know about
@@nickchapsas makes sense. Thanks for the reply.
What would be the default life cycle for this awesome class?
I don’t think I fully grasped why this strategy was better than passing a new options object. Could someone explain?
Is it possible to also make this bind to appsettings.json while also injecting it?
I did not know about the .Configure and what it actually does (shame on me) but I would literally have done the same thing if I had to implement this with the Action?...
Oo, very similar to Go's functional options pattern.
Awesome :)
Hi Nick. I just purchased 3 of your courses and I'm very excited to start with them. Any idea why they don't work with Firefox (latest version on Apple M1) but do work in Chrome?
It seems Firefox was set too strict with regard to tracking and blocked the video. Thank you for making all the amazing content
Do you contribute in any openSource project ?
What if your option values come from config (appsettings), would you just set the values in the options lambda in program.cs e.g. options.Something = ConfigurationSection[“Something”]
Nice 👍🏻
@1:45 where it starts...
Nice
nice
This guy is going to help make c# and dotnet so much more mainstream
why it's very hard to explain that is a delegate will be execute at app start?
How does your IDE know that the implementation of this Action should be in the form of 'option => { ... }'?
Where does this 'option' argument name come from?
I am assuming it’s something they have baked in. They have many quality of life features like this
it's just a lambda and it can have any name. IDE chooses last word of the class name.
@@vivan000 it actually isn’t the class name though because it doesn’t match neither the class name nor the argument name in that case
@@nickchapsas Argument is `Action`, so it suggests `option`. For argument `Action` it suggests `test`. For argument `Func` - `(first, second) => `
That's a cool feature I guess, probably too situational. Thanks for the answers!
The 12 year old in me loves the int number
Still don't see why this is better than creating and passing an immutable options type.
Because you don't have to instantiate any object, you don't need to know the type of the object, you don't need to have long names inline. It's just a better experience for me
In addition, this approach is not just limited to options. You can use it in many places. We have thousands of tests which require us to build an object and send it to some api call. Instead of manually craeting object in each tests and setting various property defaults, while only changing one or two properties. We use this approach to create an object and you can just pass an action to override required properties. The end result is really clean and SRP based code
And because it's so prevalent in .NET libraries, it's also useful to know as an API developer that users may be expecting a similar paradigm.
This approach means you're working with an already constructed options object (Thanks, abj!). So, while configuring your options, you can see what your current options are. Further, the caller doesn't need to merge options changes from each caller into the caller's internal options class: They can merely have an Action that modifies the options object directly (possibly via interface or other encapsulation method).
It took me a while to understand this as well, but the real value here is that when you have a method that takes an object you call it and that is it. If you take a delegate, you can run more than one. It can be configured from json, and this delegate, and a "standard" setup (like something that configures certain settings required for security). One great use I've found for this is json serialization settings. They are big, complicated, and have lots of things that can be modified. You might have one configure method which adds a JsonConverter, specific to the project, one which sets some security based settings (like TypeHandling), and one that sets standard settings across several projects. Certainly not the only way you could handle this, but it's a nice composable approach.
There is no audio?
Wait what?
@@nickchapsas Seems like there was a hickup on RUclips's side since yesterday all other videos worked with audio but this one didn't. Now it works fine!
Well well...even the sun has its spots!
Pretty basic stuff this time around.
Now no person above 50 can apply for an IT job because of this filter
so basicaly the javascript equalivent of
createOptions = (mine: LibOptions) => ({
...deafults,
...mine,
})
👍🏽
Is the trick that the libraries actually make me believe I'm a real developer?
Damn, for the first time watching your videos I couldn’t believe you’re explaining something so simple for so long. I actually thought I’m missing something, so I watched till the end, lol. Keep up the great work, but try not to slide too much into basics, please, because I believe many people came here for advanced stuff, which you cover the best
Fair fair. I wanted to try something more basic because some times people criticise that my videos are too advanced but it’s clear that most people like the advanced stuff so I’ll stick to that
@@nickchapsas basic is okay as long as we're learning.
@@nickchapsas Imo those "basic" things are sometimes a great reminder of what is possible and your style of presenting it makes it a great watch everytime. I especially love your videos that do not depend on third party services like AWS because it always carries a healthy dose of "I can try that out immediately" which is insanely valuable to me everytime
@@nickchapsas I have no problem re-learning basic things if they're explained well because there will always be little things I misunderstand or reasonings behind things I wasn't aware of that continue to broaden my understanding of something, I never assume I know everything there is to know about something. Even the simple things have potential to teach you great things.
In this case it was just a reminder to simplify my configuration and look into buying your courses finally : )
You have the idea but your implementation is not entirely correct.
The first issue is a question of convention. You should return the service collection so you can chain the calls.
The second issue is that Configure is guarded against a null configureOptions parameter, therefore calling AddAwesomeness() will throw ArgumentNullException.
This is unfortunate because I would rather be able to implement this pattern the way you did, rather than the more verbose implementation we may find in the framework (for instance, look for src/Middleware/Session/src/SessionServiceCollectionExtensions.cs in the repo /dotnet/aspnetcore).
The very sad part is that the underlying implementation of the constructed ConfigureNamedOptions accepts a null action (Look for /src/libraries/Microsoft.Extensions.Options/src/ConfigureNamedOptions.cs#L19 in the repo /dotnet/runtime) but the null check
was added by the extension method (Look for /src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs#L58).
This video isn't focusing on the registration convension but on the Action part. Yes in a real scenario would also return IServiceCollection. Regarding the null check, I am totally ok with extension methods overriding the underlying behavior if the ultimate goal is to guide the user to a specific implementation
i dont understand the use case of this trick
Ah Nick, i get it (money from ads) but 10 mins on a max 2 minute topic is too much :/
Oh yeah so much money from ads. I don't know where to put it all. For real though, the video doesn't need to be 10 minutes any more to get mid-rolls. 8 minutes is enough. I don't wanna rush any topic that I think needs context. If you want 2 minute video you should check out Fireship
random one... types in 69 :D :D
05:50 69 😂
69. nice.
👀👀😅
i'm not convinced about these extensions methods and actions for configuration, to me it seems too much of a logic error, something that should be solved in another way; relying on extensions method make it impossible to use a framework without reading the docs a lot, because you would never see those methods if you don't already have all the packages and usings already configured; configuring with an action to me means that separation between configuration and instantiation/logic is not well done, if you were not able to configure everything you needed before constructing the objects; i'm not saying this is heresy, i can understand not everything being perfect, but relying so much on this makes me think something is off
In fact it's the opposite. Remember that just the configuration isn't the only thing you register. In fact it is the minor thing. The main thing is to register the dependencies the way they are intented to be registered. Here is an example. Do you register your MediatR pipelines, and handlers manually? Ofc you don't, that would be silly. Instead you use the configuration extension methods because Jimmy knows better than you and he wants to make it easy for you. That's what it is about. Geting into a package should eb easy. Reading the docs should be something that you might need to do if you need something very specific. Not the general usecase. github.com/jbogard/MediatR/wiki#aspnet-core-or-net-core-in-general
ok configuration was kind of an unlucky word, since we're talking about essentially the architecture of the application; following the MediatR example: would i find Jimmy's helpful method inside the newly instantiated class? nope, it's an extension, it's a separate package (and rightly so); would it be unfathomable to find it and use it? again: no, instead i would say it's a pretty common practice at this point; if we take a more complex object, insetad, for example asp net itself, that thing uses a lot of extensions for adding features: controllers, cors, mvc, etc, but those are pretty big boys, i am not sure i like unleashing objects with that power in this way, i find it's kind of an abuse; a huge single configuration object would not solve this issue either, but at least it's searcheble, it's well defined
"everyone do it therefore it must be right" is not the way i'd like to deal with it; i know it's widespread, i use this too because how could i do otherwise, i see pros and cons, i want to get deeper in the whys and hows
Im currently using this to have each dll grab its correct configuration from a like-minded config tree entry + inject the connectionstring name in each of these so these can then be used in services (which use non orm transactions where connectionstring is needed) without needing to pass a global Configuration to these services where settingspath is the name of the executing assembly where . is replaced with : since ... passing values to this would mean that the caller would need to know the location of the setting but i want the component itself to know the location of the setting not the caller . The IConfiguration configuration then reads any configuration source e.g. jsons it can find.
public static readonly string AssemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? "X.Y.Z";
private static readonly string SettingsPath = AssemblyName.Replace('.', ':');
public static IServiceCollection AddXXX(this IServiceCollection services, IConfiguration configuration)
{
MyAwesomeoption _options = new MyAwesomeoption();
IConfigurationSection configSection = configuration.GetSection(SettingsPath);
configSection.Bind(_options);
string connectionString = configuration.GetConnectionString(_options.ConnectionName);
configuration[SettingsPath + ":ConnectionString"] = connectionString;
services
.Configure(configuration.GetConnectionString(configuration.GetSection(SettingsPath))
.AddDbContextPool(options => { options.UseSqlServer(configuration.GetConnectionString(_options.ConnectionName});
return services;
}
hmmm that's strange, using .NET7, services.Configure() doesn't seem to allow for nullable actions.
biggest take away: Options.Create ! thanks