asp.net core services initialization
Hi there,
Was wondering what the best way is to go about initializing services I add to my asp.net builder. The initialization code needs to run async methods, so the constructor is not an option here unless I do some weird stuff.
Best regards!
14 Replies
if you're able to use those services as singletons, you can construct them manually and pass them to the DI container
another option is to use the factory pattern
That's an option as well, however I don't know if it's a good idea to add them a singleton. What I did now is the following, not sure if this a good practice:
Program.cs
InitializationService
@Welles I would definitely go with the factory pattern for this. Effectively that means you'll have one interface/class responsible for creating the one you need. Since constructors can't be async, I would recommend a static async factory method on the type you're constructing as well.
If you want non-transient behavior, your factory could cache the service it creates and reuse that for later requests.
Ok I'm goign to refactor the code! I'll do some research on this factory pattern
Aaaah I see you have helped me alot in that regard haha 😄
the IDependency is a placeholder I'm guessing right?>
Yeah, just to show that you can inject whatever dependencies you might need for
CardanoService
Ok got it! 😄
Is there difference between valuetask and task?
Hmm, I'm a bit list here.
So created factory and made the change in the service (not using interfaces for now)
Then I just do builder.services.AddScope<CardanoFactory>();
Might be with the latter I'm doing something wrong.
the init function is not being called atm
K so I just get an error at the moment.
Unable to resolve service for type 'tgcDivideRoyalties.Services.CardanoService' while attempting to activate 'tgcDivideRoyalties.Controllers.WalletsController'
I'll tinker a bit 🙂
Looking at this now...
haha omg, yeah I thinkl I know. I need to inject cardanofactory in the rest of them
instead of cardanoservice
ValueTask doesn't allocate when the value is immediately available (not-async). Generally it means better performance if you're caching a value, but not a big deal if you chose to prefer
Task
.
I don't see your code for Initialize
in here, is it doing anything?yeah it is 🙂
but I don't see how this could work if I don't inject cardanofactory instead of cardanoservice in my controller.
When does the createAsync method gets triggered? On requests?
And I was also thinking, if scoped services are default with asp.net core stuff, isn't that like horrible for performance it needs to contruct each time a requests comes in?
You should inject
CardanoFactory
and then when you need to use CardanoService
that's when you'll call CardanoFactory.CreateAsync
to get your service. This is the main reason you might want to cache the value after it gets created the first time.
That is exactly correct, you'll inject the factory instead of the service. (Sorry I didn't make that clear.)
Constructing a class might be "slow" in computer terms, but it's likely faster than most of the business logic you're going to be dealing with. The general reason for scoped services is that if something goes bad during one run and a class is in an invalid state, that won't cause the next scope to fail also - you get a "clean slate".
This will also help if you have a db connection, file handle, etc. that you don't want to keep open all the time when you aren't using it to free up system resources - the scope cleanup will release those resources.
If your service doesn't have any state to it, there's no reason not to register it as a Singleton
.Ok gotcha, it's a lot to get my head around. Need to figure out to just not make it all a singleton and then use AddDbContextFactory instead of normal dbcontext which is scoped by default.
A lot of things to figure out
As I see it now, using the factory pattern, I still need to await Factory.createAsync somewhere. And I can't do that in constructors, so the issue is the same no?
the init code needs to run before the server accepts connections
What you're solving with the factory pattern in the Single Responsibility Principle - a consumer isn't responsible for creating and initializing your service. Yes, the async method needs to be called. The service provider doesn't work with async types so you will need to call an async method somewhere, but this way you keep the code base cleaner and if you're using your service in more than one place you don't have to duplicate the initialization logic.
You could think of your method in somewhat more generic terms like this:
The service
T
can be created by the factory, or just provided. You don't have to create something here - in fact, your factory can get T
from DI.
What this does is let you get T
anywhere you want it, and the initialization logic can by async if it needs to be. If not (such as DIFactory), no problem. If it is, no problem - you're returning an async type.
It's effectively solving the problem of being unable to inject an async-generated type in DI, because with DI you don't want to call new DependencyService
in some class that is a consumer of DependencyService
- that's not dependency injection at all.Well, thank you very much for the information here. I need to take a look on practical examples. 😄