The Perfect C# Console Application...Or Not.
Вставка
- Опубліковано 6 вер 2024
- There are certain best practices every C# application should follow, right? Probably DRY, SOLID, Logging, Dependency Injection, Localization, Separation of Concerns, and Unit Testing at the very least. These seem like obvious things that every application should have. In this video, we are going to do something a bit unique. I am going to show you how to do all of those things, and I am going to show you why you shouldn't always do those things. We are going to build the ultimate Hello World application. You won't want to miss it.
Full Training Courses: www.IAmTimCore...
Source Code: leadmagnets.ap...
Mailing List: signup.iamtimc...
Back-to-back C# videos from Tim Corey and Nick Chapsas. What an awesome evening!
Agreed! Excellent 👍
I'm glad you enjoy them.
awesome on 1,5 speed
@@namelastname4077If you want to watch faster turorials Nick Chapsas is the best choice for you. 😊 Tim Corey's tutotial are for the ones, who do not want to scroll back or wartch the whole video again again in order to understand.
I love this. This is an incredibly valuable lesson that usually can only be learned from making mistakes and learning the hard costly way. Been there many times. Lured by delusions of grandeur. Thanks Tim awesome video.
You are welcome.
Beautiful dependency injection, and DRY principles.
Thanks!
So Tim here just built a bionic arm that can find and scratch the back of his head even tough he has a perfectly healthy hand. I love it.
Pretty much.
Now he has a perfectly good bionic arm. Best of both worlds.
Reminds me a lot of the Extreme Programming methodology. Keep it as simple as simple as possible. If you don't absolutely(kinda) need it now don't add it, when you do you will. Fantastic video delivering two types of content in the same video. Great as always! Thanks!
You are welcome.
Pure gold resource. Priceless and valuable to infinity. I have learnt new concept and have been able to improve my C# knowledge. I have watched this video again and again which did the trick in order to understand. Keep trying and eventually it will hit your head and everything will make sense. The args part of the Main method of Program.cs was always a mystery and today you have answered this mystery. Now it makes sense why it is there! After doing 3 years of C#, I came to know this today 😅 You are the best teacher in UA-cam. Detail, precise, and meaningful. If you want to learn in how to do it in the right way, I highly recommend all of you to follow Tim Corey!
I am glad it was helpful.
I really appreciated your comments in the last 10 minutes of the video about being pragmatic. In some cases, the added complexity is justified (or even necessary). In other cases, the simple approach is best. It depends. Thanks for having the guts to say it.
You are welcome.
This is the best "Hello World" app I've seen in my life. Thank you very much.
You are welcome.
I really love your train of thought! Thanks for the confirmation in how to setup code! I am developing software for over 20 years now, and I saw a lot of 'overdesign' in these years. Great Demo! Keep up the good work... Cheers!
Thank you!
"Why is it yelling at me?" Reminds me of conversations between me and my wife.
Me: Why are you shouting?
Her: I'm not.
😂
Great demo, I was just having some trouble with getting stuff to work from a Console app, this showed me what I missed out on :)
I am also of a KISS coder, so I really liked the summary :)
Glad I could help!
Great video Tim as always. I heard you before talked in some tutorials about creating a 50-class hello world application and how over-engineering could be harmful for the project. Thanks again.
You are welcome.
Nice! As I was following along, got off track. Since this was a console app, I used the debugging profiles to change the lang property. One profile for each lang property so I could step through the code. Not sure if you've covered the debugging profiles, but it's a wonderful thing for console apps.
They are nice.
I got the error "Unhandled exception. System FormatException: The short switch '-lang=es' is not defined in the switch mappings. Anyone knows what is the issue about?
This helped me understand blazor even alot better, because all the pieces aren't wrapped up already.
Excellent!
17:32 I recently discovered that one can simply use First with a conditional, like so:
CustomText? messages = messageSets?.First(x => x.Language == language);
I haven't done any performance comparisons, but I feel like it's cleaner. One could argue that it's not as clear as Where --> First.
.FirstOrDefault() is almost always more appropriate.
@@kylekeenan3485 True but in this case we don't want a null. As mentioned in the video. We want a crash or an error been logged and not a null message. A better way would might be a default (31:20) for every label in the program if there isn't found translated one.
Good point 👍 I was going to say the same thing. Most times I see .Where(...) it is .Where().First() and you can just move the predicate from where into the first and get rid of the where. exactly as your example is.
I loved the discussion of the main App class. It was like National Pirate Day! Run args! args[i]!
lol
The ultimate "Hello World!" application? I'm in! :-)
Great!
the video is very informative, it teaches lots of important and useful concepts while saying you probably don't need that. it took me over 2 hours to watch I couldn't stop laughing every 2min.
I’m glad you enjoyed it.
A very good video. I don’t like the exception-driven development-if you can’t find the language or the key, log the error and then throw the exception to exit the program.
Thanks!
A CommandLine parser (such as commandlineparser package) to simplify the syntax of manipulating passed arguments would add to perfection!!
In fact, this was the only part of your app, where you saw some minor inconvenience!
That would be taking on a dependency for parsing one entry. That's not something I wanted to do. If I were doing lots of processing on the command line args, I might, though.
I get that this is sort of a joke, but if you want a lot of this done automatically for you, you can use the worker service template in visual studio (it's not quite the same as a normal console app, but its close). It at least gets you the dependency injection and config files. Then you will be free to over abstract and over test to your heart's content :)
Joking aside, I commonly find myself using a couple of these things in simple console apps for little temporary one off tools that are usually throw together for something. (using a config file is usually the one that rears it's head first, while unit testing is the one that happens when everyone's in denial about the fact that they built an actual product instead of a temporary one of tool).
That is a nice shortcut if you don't mind a bit of tweaking.
It is the Bloatlord of "Hello World" implementations! Thats a compliment ofc.
lol
This was too confusing for a beginner. My suggestion would be you introduce one topic at a time. Build the app in stages, i.e.
1. Bare HelloWord,
2. Introduce the ILooger,
3. Cater for exceptions,
4. Introduce JSON file for multiple languages, etc.
I am still thankful and appreciate your efforts🙏
This isn't a beginner tutorial. I've covered those topics separately.
@@IAmTimCorey I will search for them on your page and see If I can do them individually. Thank you for your response. Much appreciated.🙏
I just started learning programming with c# a little bit more than a year ago. After this video I have to say I’m not enough clever to understand and learn it. It’s way more complex that I have ever imagined. Thank you Tim! Without you maybe it took longer to realize that. I don’t know what should I do after all of this… but I have to move on. I have to give up I think. It’s really hard becouse I thought this is my last chance to achieve something; find a good job and take care of my family. I wish all of you a good luck and happy future.
That's not true. You can do it. Think of it like flying. If you were to look at the cockpit of a 747, it would look REALLY confusing and overwhelming. But that's not where you start. Even after a year of flying, you don't jump into a 747. You start with a small plane that has a few controls. You do that A LOT. Then you move up to a bit more complex plane. The same is true for becoming a software developer. What you see in this video is more like the 747 than a Piper Cub. Don't expect it to be simple. It isn't. But you don't start there. You build up to it. Start with Console applications. Learn the basics of C# syntax and practice everything A LOT. You can absolutely do it. You just need to stick with it and take it on in a logical order.
You definitely can learn it bro. Just do little by little. Don't put a timeline on it and don't compare your progress to other people because you never know what their background is, and we all learn differently. There's a lot to learn but you don't have to know it all. Pick something small and simple, then pick a small part of that and learn it. And keep track of what you're doing! Whether it's tracking time spent, keeping a journal, or keeping old console applications you've built. Just keep track so you can look back in a few months and see the progress. I promise there will be progress.
I agree with Tim and Frawsty here! This video is definitely in the intermediate to advanced category of C#. Tim has lots of great introductory videos where you should start your C# adventure. Good luck!
Being scared or overwelmed by a video like this is perfectly normal for a junior developer or a novice like yourself. Please don't be discourage but all the things happening in this video. Alot (if not all) of the things discused in this video are for advanced developers and eventhough I would used all of these techniques in my new projects, I'm working with junior developers that do not use these and they are doing just fine. Things like SOLID, Dependency Injection (DI), Sepraration of Concers and Unit testing (TDD) is all nice things to have and some of them you might be using already without knowing about it. (Start a new Blazor project and you're reready using DI out of the box). - But learn one thing at a time, play around with it and add new stuff later on with you get the hang of the things you learned so far. Learning programming is not hard. It's just time consuming and trying to learn everything at once will never go well. Go back and look at some of Tim's earlier viddeos like the "Timco retail manager series". It's a bit dated now but it will have you learn at a much slower pace but a pace that looks to be more suited for your current level.
Keep learning, you'll be alright. You have to learn when and to when not to use things. Keep practicing
Great demo about some important stuff, but summary - priceless :)
Thank you.
this might be odd, but i want to see you abstract this further towards an "enterprise" hello world app
What do you think should be added?
@@IAmTimCorey Good question! if my own company can be a guide, we need a redundant generic data layer (incase we ever change DBs), which emails the devs when it both does, and doesn't work, with a metrics dashboard for admins, and maybe dockerize it as a blazor app, and lastly have user logins that store language preferences.
im trying to think what other bloat i can think of to add... but thats all i have for now.
Thank you very-very much, Tim. You're doing a great job!
You are welcome.
"We're gonna build. . . the ultimate hello world application."
Me: puts popcorn in the microwave and turns off phone.
lol
Cracking video Tim, really informative! Keep up the good work :)
Thanks!
35 minutes in: “We have successfully created a ‘Hello World’ application!”
I chuckled.
I'm glad. I did creating the project in the first place.
Tim, around 18:00, you talk about catching an invalid language passed in (French). Given that you do teach unit coding, why are you not taking this test-first to build the API first with functionality second?
What is the reason for creating a scope in this example? Since there is only a single entry point for the app a single scope is only ever created isn't it essentially the same as a singleton?
in short, doesn't .Build().Services.GetRequiredService().Run(args) do the same thing?
I am sure I will love the video (not had time to watch it all yet). I do want to say I am very much against the default of hiding the 'main' function. That removes vital learning/context for how an application starts. It make developers less familiar with general design that is useful to know in other languages. It feels like it is deliberately making developers less portable.
At least if you see the c# main definition then when you see the c/c++ version int main(int argc, char *argv[]) , or whatever language, you can recognize what it is telling you.
I can understand that perspective, but I'm not in agreement with it. We used to say "just trust us - Main is what gets called first". There was an implicit call to the Main method. Now we say "just trust us - the code in Program.cs gets called first". Seems like a similar bit of starter knowledge. We used to have a Main explicitly stated. It was a method like all of our other methods, seemingly. However, once you start learning C#, you learned that you could not call Main like you could other methods. It was a special method. So instead of having a special method that doesn't act like other methods listed in a specific location that never changed, we now have a special class file that acts differently than other class files. I think that is clearer. Yes, there is a hidden args value. That's not idea. However, I think that this was is clearer on the fact that this method is unique in C#.
Another Golden Nugget from you Tim, thanks.
You are welcome.
Hi, some of this was very useful to me, so thank you for that. I've tried a couple of times, a couple of months apart, requesting code from the links in your UA-cam videos and not receiving anything through email. Is there maybe a newer link that just didn't get put in the videos?
Check your spam filter. Then email help@iamtimcorey.com
Nuget package being 1 tick down Is not just you. It does this to me as well, 2 months later, so still happening. And it was happening to me before I came here. So it's not like it's only happening on this entry.
Good to know. I'm going to reach out and see what can be done about it.
I have a problem. When I write " this.log.LogError(message: .., ex);" (line 33, minute 16:41) appear a red line, like an error, under this code and I don't know why. Another thing, I use Visual Studio 2022 and I can't write "_log." (underscorelog ) but the IDE replace with "This.log". Your channel is awesome and so helpfull! Thanks. PS: I'm brazilian and my English is not great, sorry.
At 13:54, we declare the _log variable on line 9. Check to see if you did something different.
I had exactly the same issue, how did you manage to solve it? I've updated the var on line 9 from This.log to _log but still. Cannot make the built because of this issue...
Hello, Tim. I hear what you are saying and yet I feel very compelled to replicate this into my local Git server for use as my new console app template.
Parts of it might be valuable for that. Just be careful how far you go.
People need to always remember that the right way is whatever the boss says is the right way, and there is no way to control the experience level of the boss. Yes, this is a good lesson in common sense as well as some good techniques. But if the boss wants unit tests, dependency injection and all else in a "Hello World" app, you have to do it that way.
There is almost always an opportunity to have an opinion, though. Even if the boss dictates what needs to be done, you can almost always at least bring up what you think is right. It might not solve the immediate issue, but if you give good advice repeatedly, you will start to earn a say in the decision-making process at most companies.
Do you have a video on how to use Serilog with unit tests?
No, sorry. What are you trying to test? That the data is getting logged? Or that you are logging specific structured logs? Because you can test the ILogger easily enough.
Instead of looping args:
string result = "en";
int i = Array.FindIndex(args, m => m.StartsWith("-lang="));
if (i > -1) result = args[i].Split('=')[1];
You forgot the Repository pattern. It's a must!
I'm not doing data access here, so it isn't a must.
@@IAmTimCorey The console is a data store! :)
Great video. I was able to learn quite a lot. Thanks for the video.
You are welcome.
Interesting debate and I agree with you Tim. Regards!
Thanks!
Thanks Tim, another awesome lesson
You are welcome.
After 35 minutes of coding and using techniques like dependency injection, logging and complying to SOLID principles: "We have successfully created a Hello World application" 🤣🤣
Glad you got a chuckle out of it.
Great video!
It just needs an OnExit event that says Goodbye World!
I'm not real clear on what events fire on exit.
You don't need an event for that. The exit of the application happens at the end of the Program.cs file. Just put your Goodbye World message there.
Cheer !!! Thank you very much. Are there any videos that use this structure along with EF6?
You are welcome and no, I don't have any.
21:06 Hypocrisy at it's pick..! 😂 love your videos. ❤
lol
Hi Tim. How do you get those highlight words inside your parenthesis that name the parameters?
Here you go: ua-cam.com/video/morBKqtqmso/v-deo.htmlsi=vQVlowE9h4RibBMA
I don't get the option to extract interface from the Quick Actions menu at 21:36.
I can find the option buried under edit -> Refactor -> Extract interface but it doesn't generate an iMessages.cs file.
Any ideas?
Have you put your cursor on the class name first?
The main thing I love is people insist we use all these fancy design patterns and practices, while also instantly forgetting probably one of the best ones, the K.I.S.S. pattern.
Yep, if a pattern isn't saving you complexity, it isn't the right fit for your application. Patterns add complexity, so they have to reduce it as well or they are a bad fit.
Great, thank you Tim
You are welcome.
I am new to your channel and with your extended absence I started thinking I came too late as you clearly took your early retirement ! This didn't surprise me given my usual bad lack, always late to the party :x
Now, with this good stuffs, not only you showed me wrong, but also it was worth the wait... Thank you Tim, keep it up
Extended absence? I took a week off last week, but I've put out an average of two videos a day for all of this year.
@@IAmTimCorey Exactly, it made well sense you took a long break given how busy you were up to now xD
As I said I am new, I joined around 7-10 days maybe. Your last video was 11 days ago... So you can see how I came to such conclusion... I started blaming myself for jinxing this beautiful channel xD
@@enissay9950 Even if Tim took 6 months of. There is still more than enough content on the page for you to learn.
@@CRBarchager Absolutely... That was not the issue... My only concern was that I was yet again late to the amazing party led by the best host :P
I am still hating myself how on earth I have not discovered this channel months/years before xD
@@IAmTimCorey I finally went through the whole video while making my own project and learned a lot, as usual. Thank you very much.
Though, I have a few comments:
+ parsing args seems to be very basic/manual, which is fine for this example given we only had 1 arg. Like other languages, is there any library/framework that provides a way to extract complex arguments with a clean HELP menu ? For instance when you have sub-options and each option has its own args, different ways of args input such as: -i file OR --input file OR -i=file, ...
+ For logging, I was expecting you to use Serilog with is more suited to build the "Perfect App" (as stated in other of your vids) and yet you settled for the basic MS logger. I wonder why ?
+ I have other comments/questions about UnitTesting BUT I bet the answer lies in you other videos on the topic which I have not checked yet
My nuget package manager does the exact same thing, at first I was confused now its just inconvenient
Interesting.
Awesome tutorial, thank you!
You're very welcome!
Outstanding video!
Thanks!
I was curious why globalusings was not used. That is supposed to always be best practices starting in .net 6.
Implicit usings are being used. Global usings are optional, if you decide they add value. They didn't, in this case.
Great video! Thanks for sharing
You are welcome.
HI, I relly live your videos. It is very useful structure for us. , But I have a question, You created a Message class to cover all messages in one place. Ok it is DRY, and testable But in a real application there are more than thousand messages. I doni think, it is good idea to create more than thousands functions for every message. How can I handle it in a real world application?
Thanks for the answer!
If you had 1,000 messages to display, you would probably store those thousand entries in a database of some kind. Then, you would create a lookup for those messages based upon a key. However, at some point you will need to tell the system which message to display. At that point, you are back to your thousand methods or at least a thousand hard-coded IDs for those messages.
Excellent question :)
Thanks.
Thanks!
Thank you!
46:43 the best part
If you would like to demonstrate source generators some day, the texts json would be a great example. :)
Thanks for the suggestion. Please add it to the list on the suggestion site so others can vote on it as well: suggestions.iamtimcorey.com/
I'm getting non-nullable property warnings(CS8618) for Language and Translation in the CustomText class. Did I miss something?
Nope. That's just the new warning in .NET 6. You can turn it off if you want.
Does anyone know which Visual Studio Theme Tim is using in this video ?
The standard dark theme.
Thx gain Tim, In some other video's i saw the the Main App started with:
var svc = ActivatorUtilities.CreateInstance(host.Services);
And then :
svc.Run();
Is it better to use the createScope method ?
Ideally, you would create the scope first but it isn't required.
How can i make a desktop app that can store an excel file in installed program files and can launch it. This means that I can write my code in the excel file and put buttons in it using only vba. So i need a way to deploy an excel file as a desktop application.
@IAmTimCorey the issue when looking for NuGet packages and the list being already scrolled.... could it be just because your VS is zoomed? By zoomed I mean that you are using big font while recording. Great video 👍
It could be, although that's still a bug. I have been able to replicate it on my laptop, although that too has the larger font size.
23:03 for me as well - this is definitely a bug.
Awesome video, I do agree with you, a developer must knows the best practices but must not have it in every project. However, the developer must be careful with being pragmatic like this, depending on the company or the position of this dev, this may sounds lazy and lack of dev skills.
Part of pragmatism is knowing where you are working. Your code needs to fit your environment as well as the actual code situation. A simple example is my coding styles changed based upon which organization I worked for. I had my preferences, but they were superseded by the standards of the organization. The same will be true for code patterns you use. However, if you are building complexity just to not look lazy or incompetent, the people doing the evaluation are incompetent. That's not uncommon and you need to do what works for your organization. The best code, though, is simple even when it does complex tasks.
@@IAmTimCorey I can't believe you replied me, I love what you're doing for the dev community, the quality of your free content is amazing. I'm from Brazil and it's hard to buy anything in dollars due to the local currency, the value increased 5 or 6 times.
Yeah I totally agree with you, we have a saying here, it goes like "do not build a bazooka to kill an ant".
Hi Tim, the request isn't related to video.
I have a class library with repository, but this repository needs settings that are in Ui project, because are stored in settings.settings file and I can't pass this static class to repository, so I have created a dictionary with informations and passed to the repository, is a good way to proceed or you suggest another strategy?
Thanks in advance.
I got Trojan:AndroidOS/Multiverze warning from defender on the project download.
That’s a false positive.
I noticed that you didn't create an interface for App and just instantiated the concrete type directly. Is this a case where an interface is overkill or is it a shortcut and a real app should have the IApp interface?
Nothing depends on App so there isn't a benefit to adding an interface (I don't need to mock it), so I didn't create one. So yes, the interface was unnecessary.
@@IAmTimCorey Thanks. I like the App class. I have a console app that I have separated into two classes. An API class, to deal with calls to an external API and a "business logic" class to deal with internal logic. There is still a fair amount of plumbing to co-ordinate these classes, which was originally in the Program class Main method. An App class with a single Run method is just the ticket for keeping the Main method clean. I don't think it is overkill as the Main method has enough to do wiring up the app. I probably would have automatically created an IApp interface, but as you pointed out, it is unnecessary
10 PRINT "Hello World!"
😆
20 GOTO 10
My first application!
@@IAmTimCorey And I’m sure version two was
10 PRINT “Tim is cool!!”
20 GOTO 10
😉
great tutorial...so sad for me cause im still using vs 2019
Why haven't you been able to upgrade?
Is this enterprise version of HelloWorld?
lol sort of.
That's the way BMW creates a Car 😂
There is something to be said for that, though. It is nice to have all of the features.
Great video but would have appreciated chapters to jump to
I ask my audience to create chapters if they want them. Instead of spending more time on a video, I move on to the next video, thus creating more content.
Hi Tim.
I totally agree with you that adhering to all practices and principles should come with requirement and not suggestions. I like that phrase so much I put it on my social media status 😂.
I inherited a project recently that implemented mediator pattern and a few other techniques that could have been avoided, don't get me wrong I think mediator pattern is fantastic but just like you I also like to write simple code to the point that a new developer should be able to understand my code without stress hence my preference to avoid unnecessary complexity and techniques. I think a lot of developers start to adopt complexity into projects from inception of the project because they always want to create room for the extensibility when the project grows to a massive scale, but not all projects grow to that point. How then can a developer identify a project that may or may not require complex principles and patterns? because introducing such principles after the early stage of a project could require more work.
Any discount codes available for your courses?
Not now, but if you are on the mailing list, you will hear about upcoming discounts.
@@IAmTimCorey Okay thanks for reply . Hopefully there's upcoming promotion for haloween , been wanting to buy for a while :)
The idea was nice but execution not so much, the args use is borderline terrible, if you want to use args, try CommandLineParser. also, why would you pass the language as parameter as opposed to reading from the runtime environment with an option to change the language with settings, then you could also deserialize all translations once at runtime to massively improve performance. Classes that you don't plan on inheriting from, you should declare as sealed from the get-go also to improve performance. And while json might be convenient it is also very easy for the client to mess with it. I think it would be better to hard core all the json into one constant string and then deserialize that string (which would be very easy with .NET 7 raw string literals).
1) CommandLineParser is a dependency. Don't take on dependencies you don't need. I didn't need that dependency to read one parameter.
2) You read the language as a passed-in parameter for a few reasons - first, this allows your application to be flexible and not tied to the OS. Second, you suggest that the user could have an option to change the language - that's what the command-line argument is. I just didn't pull the initial OS language. Third, this is a console application. Command-line options are the most common way to set initial values.
3) Sealing the class is a premature optimization. Yes, sealing a class MAY improve performance, but that is situationally dependent and based upon the usage of your class. For instance, it may save you a millisecond (just made-up number) but unless you were using 1,000+ instances of the class, that isn't noticeable. Therefore, sealing it is not necessary and is potentially harmful.
4) The whole point of using a JSON file is that clients can mess with it. This is a VERY common pattern, where the developers create the primary language values and the translation file. Then, they can turn the JSON file over to translators to add in the other languages. I've done this multiple times in production for business applications. The point here, too, was that you could later add in an additional language (I demonstrated French) without updating the application.
Hey Tim a complete tutorial on NSIS and Innosetup would be a great idea to include in your library.
Thanks for the suggestion. Please add it to the list on the suggestion site so others can vote on it as well: suggestions.iamtimcorey.com/
BOLD!
Thanks!
48:30 Salut tout le monde means "Hello everyone". Remove tout and get "Hello world".
Thanks for sharing.
Am I the only one who finds it ironic that it takes an hour video to explain yet another best practice- YAGNI (You Aren't Gonna Need It)? I do love the video, but the irony amuses me.
That's why I introduced it and summarized it at the end like I did. There were two points to the video. First, you got to see a lot of best practices in building a complex application that are necessary when your application gets to a certain size. However, secondly and more importantly, the video was a demonstration of how just applying "best practices" blindly is foolish. You need to make the right choice for your specific situation rather than trying to make every situation fit the same mold.
If your like me, just skip to 50 minute mark
Dependeny injection is so f------ing overrated
Why do you think that?
Thanks for the video. Is there a way to log messages to a separate console? I found a few examples where the instruction .AddConsole() is used, but it does not seem to create a saperate console..
No, the console is the console. You could log to another location, though (SQL, text file, etc.)