Reputation: 2103
I'm having a hard time wrapping my head around how this should work. This is for a .NET Core application.
I have a cryptography library that defines ICryptoService and an implementation class DefaultCryptoService, I also have a CryptoSettings class that I am populating from appsettings.json in my startup. So:
public class DefaultCryptoService : ICryptoService {
private CryptoSettings cryptoSettings;
public DefaultCryptoService(CryptoSettings settings){
cryptoSettings = settings;
}
}
and in my Startup:
services.AddSingleton<ICryptoProvider>(new DefaultCryptoProvider(cryptoSettings));
So far so good. But now imagine I have a data layer in another class library that needs to make use of these cryptography features as well as connect to the database. It's basically repository classes that implement a base IRepository pattern. My controllers will call the IRepository implementations to work with the database as needed, but how does that layer get access to the ICryptoProvider implementation that I initialized in Startup?
HomeController --> UserRepository --> needs access to both DatabaseSettings (from config, just like my crypto ones), as well as the implementation of ICryptoProvider that I set in startup so it can encrypt/decrypt user info as needed.
I feel like I'm on the cusp of putting this all together but I'm not understanding the correct way to chain it all together.
Upvotes: 0
Views: 528
Reputation: 12619
You just register your types and provide public constructor that defines requirements and if those are available magic will happen. Taking you crypto provider you could have register it differently:
services.AddSingleton(cryptoSettings);
services.AddSingleton<ICryptoProvider, DefaultCryptoProvider>();
If you write it this way it will be resolved. You actually do not have to call constructors directly anywhere in your code it will be done for you by dependency resolution.
Also check out different methods on services collection - not all classes should be singletons.
class UserRepository : IUserRepository{
UserRepository(IDatabaseContext context, ICryptoProvider provider){
}
}
and register it:
services.AddTransient<IUserRepository, UserRepository>();
you need to add database context as well for this repository to be resolved. And this can be very deep structure you can imagine that controller is not working directly with repositories but there is some layer of services:
class UserService : IUserService{
UserService(IUserRepository userReposiotry, IMembershipRepository membershipRepository){
}
}
and controller constructor is like this:
UserController(IUserService service){
}
From controller point of view you do not care that two repositories are required and each of those requires database context and the crypto provider. Controller just needs to verify payload and if valid the job is delegated to service for processing. In turn service does not care about internal logic and requirements of repositories.
As for registrations it is series of calls on services
collection AddTransient<Interface,Class>()
. And yes you will get 1:1 relationship between interfaces and classes. This is a good thing for unit testing.
Upvotes: 1