C
C#14mo ago
PatrickG

❔ question about console app host.RunAsync();

In this example: https://learn.microsoft.com/en-us/dotnet/core/extensions/logging-providers using Microsoft.Extensions.Hosting; using IHost host = Host.CreateDefaultBuilder(args).Build(); // Application code should start here. await host.RunAsync(); I am wondering what is the point of host.RunAsync(); because I have tested without it and there is no difference.
Logging providers - .NET
Learn how the logging provider API is used in .NET applications.
108 Replies
mtreit
mtreit14mo ago
I think that's a confusing code example because it omits where you actually add your service code. A real worker service (which they claim to be using as an example) looks more like this:
using WorkerService;

IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();

await host.RunAsync();
using WorkerService;

IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();

await host.RunAsync();
Where the Worker class then has an implementation of a background service loop And in that real world scenario, if you never call Run or RunAsync that service never actually starts running.
PatrickG
PatrickG14mo ago
yea, for a background worker i know that but what about for a plain console app like for exemple using Microsoft.Extensions.Hosting; using IHost host = Host.CreateDefaultBuilder(args).Build(); Console.WriteLine("Hello World") await host.RunAsync();
mtreit
mtreit14mo ago
For a plain console app you wouldn't normally use a host builder at all I think. Their example is supposed to be based on a worker service, not a plain console app.
Jayy
Jayy14mo ago
i mean there are many reasons youd need a generic host in a console app
PatrickG
PatrickG14mo ago
I'm just trying to understand how I would use dependancy injection for a simple console app I guess I just configure the services and then aquire an instance
mtreit
mtreit14mo ago
I don't think the .NET team really expected people to want to do that. At least they certainly didn't make it simple or obvious.
Jayy
Jayy14mo ago
here this is just some nice syntax to help you not call GetAwaiter manually hmm? this is literally what the generic host is for
PatrickG
PatrickG14mo ago
ok that's possible
Jayy
Jayy14mo ago
this is very standard stuff literally this is what GenericHost is designed for
mtreit
mtreit14mo ago
Well, the templates for console apps don't use such a thing at all.
PatrickG
PatrickG14mo ago
I mean a console app could be very complex and one might want to use dependancy injection to make it more testable no?
Jayy
Jayy14mo ago
no cuz they dont need to
Jayy
Jayy14mo ago
.NET Generic Host - .NET
Learn about the .NET Generic Host, which is responsible for app startup and lifetime management.
Jayy
Jayy14mo ago
first paragraph in docs theres ton of reasons you might have a long running console app that needs a full host but it isnt necessarily a web thing
mtreit
mtreit14mo ago
That still shows the worker service example not a simple way to do it in a console app. They don't make it obvious how to do it in a simple console app that isn't some kind of service.
Jayy
Jayy14mo ago
scroll down its the same thing tho like you just register the host deps, and run it @PatrickG what do you mean youve tested without the RunAsync?
PatrickG
PatrickG14mo ago
hmmm actually i tested aquireing settings from appsettings.json without host.RuunAsync i dont think i tested DI tho but not sure i might have once
Jayy
Jayy14mo ago
elaborate on this cuz its not making much sense nothing happens until you run the host well, it depends but nothing like run happens until you run it when
PatrickG
PatrickG14mo ago
hold on IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddTransient<RandomClass>(); }) .Build(); //retrive instance of RandomClass call some function from random class i think this would work without host.RunAsync();
Jayy
Jayy14mo ago
err how like what do you mean "work" that does nothing
PatrickG
PatrickG14mo ago
how so well lets say RandomClass.WriteSomeThing(); it would write something.. and the instance was created by the DI framework
Jayy
Jayy14mo ago
well this is static so its completely unrelated to DI
PatrickG
PatrickG14mo ago
no not static
Jayy
Jayy14mo ago
no it wouldnt you never called it and it was never created
PatrickG
PatrickG14mo ago
if you do
Jayy
Jayy14mo ago
Jayy
Jayy14mo ago
absolutely nothing happens here
mtreit
mtreit14mo ago
This conversation is proving my point that it's very non-obvious how to use things like dependency injection in a plain console program.
PatrickG
PatrickG14mo ago
RandomClass rdm = provider.GetRequiredService<RandomClass >();
Jayy
Jayy14mo ago
i mean its not the MOST obvious thing in the world, but if you have any idea about how a host works its not hard ahh, so this is a VERY different thing
Henkypenky
Henkypenky14mo ago
I agree, I have a template at the ready because I always forget.
Jayy
Jayy14mo ago
@PatrickG so the problem here is that you arent actually... using the host at all
PatrickG
PatrickG14mo ago
using IServiceScope serviceScope = services.CreateScope(); IServiceProvider provider = serviceScope.ServiceProvider; RandomClass rdm = provider.GetRequiredService<RandomClass >();
Jayy
Jayy14mo ago
yes thats all good, however thats completely unrelated conceptually to the Host your just using the container directly
Henkypenky
Henkypenky14mo ago
maybe a distinction between the host and DI will be nice, like is the host needed at all or just DI?
Jayy
Jayy14mo ago
you dont need a host to do that
PatrickG
PatrickG14mo ago
ok so that was my point thats pretty much how u use DI in a bland console app dont need host.RynAsync()
Jayy
Jayy14mo ago
Host = While loop that runs and does stuff Container = Grab bag of dependencies that allows you to resolve stuff easily if you want to use DI in a console app you should probably just use the full host, you get alot more power youd implement IHostedService as your entry point
PatrickG
PatrickG14mo ago
implement where
Jayy
Jayy14mo ago
anywhere you want, then you register it with ur host
PatrickG
PatrickG14mo ago
oh yea ive done that before i did a worker but it could run as a console app too
mtreit
mtreit14mo ago
It's basically taking the worker template and then hacking it to not have a loop that runs forever.
Jayy
Jayy14mo ago
a host runs until its stopped unless ur talking about the di thing
mtreit
mtreit14mo ago
Yes, which is another reason it feels like the wrong way to do a console app at first glance. Most console apps don't have the "I will run until stopped" thing At least a very large number of them don't
PatrickG
PatrickG14mo ago
ok but lets say you are making a console app for a huge ETL process you just code it without DI ?
Jayy
Jayy14mo ago
i mean generally this kinda thing would need a scheduler or trigger etc we ran our jobs with quartz which we spun up in a Host
mtreit
mtreit14mo ago
I would personally probably not use DI, but then again I mainly use DI because I am using other stuff (like writing an ASP.NET Core web service) that sort of forces you down that path. I mean, I would use DI in the purest sense: passing in interfaces in constructors.
PatrickG
PatrickG14mo ago
because the point of DI is to make more testable you can easily write a test that replaces the whole database with just a dummy service
Jayy
Jayy14mo ago
what are you scheduling this ETL with?
PatrickG
PatrickG14mo ago
task manager i mean
JakenVeina
JakenVeina14mo ago
are you talking about DI here, or IoC?
PatrickG
PatrickG14mo ago
task scheduler
mtreit
mtreit14mo ago
Let me rephrase: I would probably use DI but not use a DI framework.
PatrickG
PatrickG14mo ago
IoC and DI is the same no ?
JakenVeina
JakenVeina14mo ago
nope
Jayy
Jayy14mo ago
no
PatrickG
PatrickG14mo ago
how aso
Jayy
Jayy14mo ago
IoC is the thing that resolves DI dependencies
JakenVeina
JakenVeina14mo ago
IoC is a pattern for orchestrating DI
Jayy
Jayy14mo ago
DI is literally just "i wrote a constructor that takes a class as an argument
mtreit
mtreit14mo ago
"interface", usually
JakenVeina
JakenVeina14mo ago
usually
PatrickG
PatrickG14mo ago
i thought DI ment, instead of instanciating a class mself, i let a framework do it
Jayy
Jayy14mo ago
ya thats what i meant, ive been doing python DI lately so class is fine there
mtreit
mtreit14mo ago
That's not what DI is. DI is a much simpler and basic concept.
Jayy
Jayy14mo ago
im actually writing a powerpoint right now as we speak about how to do DI in Python correctly
mtreit
mtreit14mo ago
Take your dependencies as input.
JakenVeina
JakenVeina14mo ago
that is a much better description of IoC
PatrickG
PatrickG14mo ago
oh ok
Henkypenky
Henkypenky14mo ago
share it if you can, seems fun to read
JakenVeina
JakenVeina14mo ago
anyway, no, there's nothing wrong with using IoC in a console app the bigger the better, IMO
Jayy
Jayy14mo ago
ya i can share what i have, most of my lectures at work are more discussion based then slide based so the slides are pretty small. But basically cuz my new team is all fastapi im just talking about the fastapi container its like a babys first DI container lol agreed i default to it for everything, one pattern, easy to understand etc
Henkypenky
Henkypenky14mo ago
i've never seen it done in python
mtreit
mtreit14mo ago
DI: super useful and you should totally use it. IoC: overrated mechanism that complicates everything
Jayy
Jayy14mo ago
how do you resolve complex dependency graphs?
JakenVeina
JakenVeina14mo ago
we have exactly this setup at work, actually, a console app that is called by Windows Task Scheduler for a bunch of background stuff. Different CLI arguments for different tasks. This app currently does NOT use IoC at the root, just cause of how old it is, but some of tbe newer tasks that have been added to it do, and eventually I'd like to see it all changed over
PatrickG
PatrickG14mo ago
ok well now from what you explained, DI is just a simple coding practice that ive been doing forever
Jayy
Jayy14mo ago
correct $di
MODiX
MODiX14mo ago
Dependency injection is really just a pretentious way to say 'taking an argument' See: http://blog.ploeh.dk/2017/01/27/dependency-injection-is-passing-an-argument/
Dependency injection is passing an argument
Is dependency injection really just passing an argument? A brief review.
Jayy
Jayy14mo ago
we have a tag when
mtreit
mtreit14mo ago
I just...pass interfaces around. When I need a class to inject into something...I new one up.
Jayy
Jayy14mo ago
what happens when that class needs 4 other classes and they need 10 other classes combined stuff gets deep factory methods?
Henkypenky
Henkypenky14mo ago
maybe you can take a page from azure functions when
Jayy
Jayy14mo ago
The fast api version is basically the factory pattern with automatic calling at the reqeust entry point
JakenVeina
JakenVeina14mo ago
it's entirely possible that console programs don't get that complex, in mtreit's scenarios
PatrickG
PatrickG14mo ago
each class has a constructor that passes the required dependancies to the child class or something
mtreit
mtreit14mo ago
It depends naturally, but I'm not afraid to just write startup code and wire things up myself. It's simple to read. There's no "black magic" where I have to track down where some class is even coming from. I do like simplicity and I like to minimize coupling between types so I tend to try to have simple, non-complex dependency graphs.
Jayy
Jayy14mo ago
@Henkypenky this should give you the gist
# Entry root
@router.post("/validate")
async def validate(
image_scanner: Annotated[ImageScanner, Depends(get_image_scanner)]
) -> AdmissionsResponse:
# Entry root
@router.post("/validate")
async def validate(
image_scanner: Annotated[ImageScanner, Depends(get_image_scanner)]
) -> AdmissionsResponse:
def get_settings() -> Settings:
return Settings()


def get_image_scanner(settings: Annotated[Settings, Depends(get_settings)]) -> ImageScanner:
return ImageScanner(settings)
def get_settings() -> Settings:
return Settings()


def get_image_scanner(settings: Annotated[Settings, Depends(get_settings)]) -> ImageScanner:
return ImageScanner(settings)
class ImageScanner:
def __init__(self, settings: Settings):
self._settings = settings
class ImageScanner:
def __init__(self, settings: Settings):
self._settings = settings
basically a more manual register
PatrickG
PatrickG14mo ago
anyways so basically I just need to do DI to get the benefits of easily testable code dont need to use IoC right
mtreit
mtreit14mo ago
Yes
Jayy
Jayy14mo ago
yes but it depends on ur usecase etc if you use the container
PatrickG
PatrickG14mo ago
well the container is just for if you are too lazy to replace all the instances of a class in your code you replace it only at one place with IoC but that doesn't take that long most of the time anyways search and replace..
Jayy
Jayy14mo ago
huh no thats not the benefit of IOC at all
PatrickG
PatrickG14mo ago
im pretty sure its stated as one of the main benefits of it at least in the microsoft docs
Jayy
Jayy14mo ago
that doesnt make much sense lol
PatrickG
PatrickG14mo ago
well yes it does
Jayy
Jayy14mo ago
a rename is already a one click process IOC doesnt change that unless you meant change the underlying implementation
PatrickG
PatrickG14mo ago
Dependency injection - .NET
Learn how to use dependency injection within your .NET apps. Discover how to registration services, define service lifetimes, and express dependencies in C#.
Jayy
Jayy14mo ago
idk what im supposed to gain from that
PatrickG
PatrickG14mo ago
The class creates and directly depends on the MessageWriter class. Hard-coded dependencies, such as in the previous example, are problematic and should be avoided for the following reasons: To replace MessageWriter with a different implementation, the Worker class must be modified. If MessageWriter has dependencies, they must also be configured by the Worker class. In a large project with multiple classes depending on MessageWriter, the configuration code becomes scattered across the app. This implementation is difficult to unit test. The app should use a mock or stub MessageWriter class, which isn't possible with this approach.
Jayy
Jayy14mo ago
ya basically interafaces which isnt what you said really, or i just misunderstood it
PatrickG
PatrickG14mo ago
anyways so what would you say is the main benefit of IoC
Jayy
Jayy14mo ago
Not having to create a 100 instances of different classes to create an instance of something lol
PatrickG
PatrickG14mo ago
so just lighten the code?
Jayy
Jayy14mo ago
i mean ya lol thats a pretty fucking big lightening
PatrickG
PatrickG14mo ago
yea makes sense anyways time for me to go to bed thx all
Henkypenky
Henkypenky14mo ago
thanks for sharing!
Accord
Accord14mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server
More Posts