I have a simple HostBuilder setup:
private void BuildServices(IServiceCollection services) {
services.AddHttpClient<IApiClient, ApiClient>();
}
... where:
class ApiClient : IApiClient {
public ApiClient(HttpClient httpClient, ClientOptions options) {
// do stuff
}
}
I have a ClientOptions object configured. How do I register it so it will be injected into ApiClient?
I am going to give this a shot, but I am not 100% sure this is what you are asking for.
Typically, you would inject it in to your pipeline. So let's say you have ClientOptions set up this way:
ClientOptions.cs
public interface IClientOptions
{
int DeriveSomeValue();
}
public sealed class ClientOptions : IClientOptions
{
public int DeriveSomeValue() => 42;
}
You would then inject it in to your pipeline:
services.AddTransient<IClientOptions, ClientOptions>();
// or: services.AddScoped<IClientOptions, ClientOptions>();
// or: services.AddSingleton<IClientOptions, ClientOptions>();
Once that is done, you can inject it in to IApiClient like so:
public sealed class ApiClient : IApiClient {
private readonly IClientOptions _clientOptions;
public ApiClient(HttpClient httpClient, IClientOptions options) {
_clientOptions = options;
var myDerivedValue = _clientOptions.DeriveSomeValue();
}
}
I am hoping that's what you are asking, if not, please let me know and I can clarify.
Related
How can I inject a specific setting (of possibly many) from an array appSettings.json in a C# .NET Core Web API, based on a runtime input value?
appSettings.json:
{
"SettingProfiles": [
{
"Name": "Profile1",
"SettingA": "SettingAValue1",
"SettingB": "SettingBValue1"
},
{
"Name": "Profile2",
"SettingA": "SettingAValue2",
"SettingB": "SettingBValue2"
}
...
}
Settings Classes:
public class Settings {
public List<SettingsProfile> SettingsProfiles { get; set; }
}
public class SettingsProfile {
public string Name { get; set; };
public string SettingA { get; set; };
public string SettingB { get; set; };
}
Service class:
public class MyService : IMyService {
private readonly SettingsProfile _Profile;
public MyService(SettingsProfile profile) {
_Profile = profile;
}
public void DoStuff() {
Console.WriteLine($"Setting A: {_SettingsProfile.SettingA}, Setting B: {_SettingsProfile.SettingB}")
}
}
The user will enter the setting name they want to apply. I am unsure how to do this if the service is configured in Startup.cs, at which point I don't yet have the setting to use.
I am understanding that "newing" the service would be bad practice, although that's the only way I can figure out how to make it work:
public class MyController {
private readonly Settings _Settings;
public MyController(Settings settings) {
_Settings = settings;
}
public IActionResult DoStuff(profileName) {
SettingsProfile profile = _Settings.Where(profile => profile.Name == profileName);
MyService service = new Service(profile);
}
}
I'm obviously missing something, but I've been watching YouTube videos on Dependency Injections and reading StackOverflow until my eyes bleed, and haven't figured it out yet. Can someone help me with a pattern that I should be following?
This is how I think it should work.
It will be a lot cleaner if you use another pattern: Factory.
interface ISettingServiceFactory{
MyService GetService(string profileName);
}
class SettingServiceFactory: ISettingServiceFactory
{
MyService GetService(string profileName){
}
}
Now you can implement GetService in two ways.
The first one is by creating new as you did in the controller and is not that bad as this is the purpose of the factory. In this way you kind of move that logic somewhere else.
A second one would be a bit uglier but something like this
interface ISettingServiceFactory{
MyService GetService(string profileName);
void SetCurrentProfile(SettingsProfile profile);
}
class SettingServiceFactory: ISettingServiceFactory
{
private IServiceProvider _serviceProvider;
private Settings _Settings;
public SettingServiceFactory(IServiceProvider serviceProvider,Settings settings){
_serviceProvider = serviceProvider;
_Settings = settings;
}
MyService GetService(string profileName){
var service = _serviceProvider.GetRequiredService<MyService>();
var profile = _Settings.Where(profile => profile.Name == profileName);
service.SetCurrentProfile(profile);
return service;
}
}
This second approach would be useful only if the implementation of MyService has a lot of other dependencies by itself and if you want to avoid new at any cost.
In both cases you will inject the factory in the controller
public MyController(ISettingServiceFactory settingServiceFactory) {
_settingServiceFactory= settingServiceFactory;
}
public IActionResult DoStuff(profileName) {
MyService service = _settingServiceFactory.GetService(profileName)
}
I have class with constructor for logging and for access to config:
public class SendEmaiServiceProvider
{
private readonly IConfiguration _config;
private readonly IWebHostEnvironment _env;
private readonly ILogger<SendEmaiServiceProvider> _logger;
private readonly string _fromEmailAddress;
public SendEmaiServiceProvider(IConfiguration config, IWebHostEnvironment env, ILogger<SendEmaiServiceProvider> logger)
{
_config = config;
_env = env;
_logger = logger;
_fromEmailAddress = _config.GetValue<string>("AppSettings:Email:FromEmailAddress");
}
public void SayHi()
{
Console.WriteLine("Hi");
}
}
The question is - How to call method SayHi from another class without pushing logger, env and config?
No I initialize new object with parameters, but I sure that it is wrong:
var sendEmaiServiceProvider = new SendEmaiServiceProvider(_config, _env, _logger);
sendEmaiServiceProvider.SayHi();
I can create an empty constructor but I will not have _fromEmailAddress value.
Looks like this is a netcore website. Assuming so, then:
Create an interface for the dependency.
Register the dependency in Startup.cs
Request the dependency as needed from the netcore DI.
public interface ISendEmaiServiceProvider
{
void SayHi()
}
public class SendEmaiServiceProvider : ISendEmaiServiceProvider
{
public void SayHi() { }
}
Then in Startup.cs:
public void ConfigureServices( IServiceCollection services )
{
services.AddScoped<ISendEmaiServiceProvider, SendEmaiServiceProvider>();
}
Then in the Controller (or wherever else DI is used), request it in the .ctor and all the dependencies for SendEmaiServiceProvider will be filled automatically by DI.
public class HomeController : Controller
{
public readonly ISendEmaiServiceProvider _emailService;
public HomeController( ISendEmaiServiceProvider emailService )
{
_emailService = emailService
}
}
That should get you going.
You should use dependency injection here. Better you create an interface here and resolve your 'SendEmaiServiceProvider' on the startup. And then use the interface instead of creating a new instance for SayHi() method.
public interface YourInterface
{
void SayHi()
}
public class SendEmaiServiceProvider : YourInterface
{
public void SayHi()
{
//your code
}
}
On your startup,
public void ConfigureServices( IServiceCollection services )
{
services.AddScoped<YourInterface, SendEmaiServiceProvider>();
}
On your controller/service,
public class YourController : Controller
{
public readonly YourInterface _emailSenderService;
public HomeController( YourInterface emailSenderService )
{
_emailSenderService = emailSenderService
}
public IActionResult SayHI()
{
_emailSenderService.SayHi()
}
}
I'm trying to set up multiple hosted services that all share some base functionality, but differ in some configuration that they use to set things up (like connection strings, urls, values, or whatever). I would like to achieve something like this in Program.cs:
services.AddHostedService<CustomHostedService1>(config =>
{
config.Value = 1234;
});
services.AddHostedService<CustomHostedService2>(config =>
{
config.Value = 5678;
});
The base class and derived classes would look something like this:
public abstract class BaseHostedService : BackgroundService
{
public MyConfigObject Config { get; set; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
ConfigureBaseClass(Config);
DoWork();
}
private void ConfigureBaseClass(MyConfigObject config)
{
// Use config to do something
}
public abstract void DoWork();
}
public class CustomHostedService1 : BaseHostedService
{
private readonly ISomeService1 _someService;
public CustomHostedService1(ISomeService1 someService)
{
_someService = someService;
}
public override void DoWork()
{
// Do some work here
_someService.DoWork();
}
}
public class CustomHostedService2 : BaseHostedService
{
private readonly ISomeService2 _someService;
private readonly ILogger<CustomHostedService2> _logger;
public CustomHostedService2(ILogger<CustomHostedService2> logger, ISomeService2 someService)
{
_someService = someService;
_logger = logger;
}
public override void DoWork()
{
// Do some work here
_someService.DoWork();
}
}
What would be a good pattern to achieve this? Note that the two derived classes might have different dependencies.
I have 2 interfaces:
public interface IPedidoService
{
UsuarioDrogueria CUsuarioDrogueria(string userId, int idDrogueria);
List<PedidoComboProducto> CPedidosCombosProductos(int idcombo, int idPedido);
}
public interface IEmailService
{
void SendEmailAttachment(string email, string subject, string archive);
void SendNotificationEmail(List<Pedido> pedidos, string email, Drogueria drog);
void SendNotificationEmailADM(Pedido pedido) ;
}
I want to use the functions from IEmailService inside IPedidoService, so I inject it in its constructor when I create the respository.
public class PedidoService : IPedidoService
{
private readonly IEmailService emailService;
public PedidoService(IEmailService e)
{
this.emailService = e;
}
}
Up until here everything works fine, but when I try to do reverse the roles (IPedidoService functions inside IEmailService):
public class EmailService : IEmailService
{
private readonly IPedidoService pedidoSettings;
public EmailService(IPedidoService p)
{
this.pedidoSettings = p;
}
}
I end up getting this exception:
System.InvalidOperationException: A circular dependency was detected for the service of type
'EnvioPedidos.Data.Abstract.IPedidoService'.
EnvioPedidos.Data.Abstract.IPedidoService(EnvioPedidos.PedidoService) ->
EnvioPedidos.Data.Abstract.IEmailService(EnvioPedidos.EmailService) ->
EnvioPedidos.Data.Abstract.IPedidoService
Can anybody help me trace the issue here?
A simple way is to use Lazy<T> class which is based on this blog:
Custom extension method:
public static class LazyResolutionMiddlewareExtensions
{
public static IServiceCollection AddLazyResolution(this IServiceCollection services)
{
return services.AddTransient(
typeof(Lazy<>),
typeof(LazilyResolved<>));
}
}
public class LazilyResolved<T> : Lazy<T>
{
public LazilyResolved(IServiceProvider serviceProvider)
: base(serviceProvider.GetRequiredService<T>)
{
}
}
Configure in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//services.AddSingleton<IPedidoService, PedidoService>();
//services.AddSingleton<IEmailService, EmailService>();
services.AddLazyResolution();
}
Change your implements class:
public class PedidoService : IPedidoService
{
private readonly Lazy<IEmailService> emailService;
public PedidoService(Lazy<IEmailService> e)
{
this.emailService = e;
}
//...
}
public class EmailService : IEmailService
{
private readonly Lazy<IPedidoService> pedidoSettings;
public EmailService(Lazy<IPedidoService> p)
{
this.pedidoSettings = p;
}
//...
}
When you have 2 classes, they cannot reference each other by dependency injection. This is called a circular dependency, as shown by your error. You need a 3rd class that references both services and you can use the methods there.
public class PedidoService
{
public PedidoService()
{
}
}
public class EmailService
{
public EmailService()
{
}
}
public class Container
{
private readonly EmailService emailService;
private readonly PedidoService pedidoService;
public Container(EmailService emailService, PedidoService pedidoService)
{
this.emailService = emailService;
this.pedidoService = pedidoService;
}
//use the services here
}
I am really struggling to properly refactor my class so I can inject it.
This is the class I am talking about:
internal class OCRService : IDisposable, IOCRService
{
private const TextRecognitionMode RecognitionMode = TextRecognitionMode.Handwritten;
private readonly ComputerVisionClient _client;
public OCRService(string apiKey)
{
_client = new ComputerVisionClient(new ApiKeyServiceClientCredentials(apiKey))
{
Endpoint = "https://westeurope.api.cognitive.microsoft.com"
};
}
public async Task<List<Line>> ExtractTextAsync(byte[] image)
{
//Logic with _client here
}
}
I really don't know where to Initialize the ComputerVisionClient. I am thinking of the following options:
Make ComputerVisionClient a public property which can be set after injecting.
Putting the apikey in a config file and then read it in the constructor.
The problem is that I want to mock this service but when I mock it it still calls the constructor which connects to the ComputerVisionClient.
Depending on the rest of your architecture, you have a few options. The simplest is to inject the ComputerVisionClient (or IComputerVisionClient if you can create one) into the constructor, and mock it in your tests.
public class OCRService : IOCRService, IDisposable
{
public OCRService(IComputerVisionClient client)
{
_client = client;
}
}
If, for some reason, you must create the client in the constructor, you can create a factory and inject that:
internal class ComputerVisionClientFactory : IComputerVisionClientFactory
{
public GetClient(string apiKey)
{
return new ComputerVisionClient(new ApiKeyServiceClientCredentials(apiKey))
{
Endpoint = "https://westeurope.api.cognitive.microsoft.com"
};
}
}
// ...
internal class OCRService : IOCRService, IDisposable
{
public OCRService(string apiKey, IComputerVisionClientFactory clientFactory)
{
_client = clientFactory.GetClient(apiKey);
}
}
As #maccettura suggested, you can also further abstract away the apiKey by creating an IOCRServiceConfiguration that contains the logic for getting the key, and pass that into the constructor for either OCRService or ComputerVisionFactory, depending on your architecture. Naively:
internal class OCRServiceConfiguration : IOCRServiceConfiguration
{
public OCRServiceConfiguration(string apiKey)
{
ApiKey = apiKey;
}
public string ApiKey { get; }
}