| 
									
										
										
										
											2025-07-01 12:27:28 -03:00
										 |  |  | using System.Text; | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  | using Mercados.Infrastructure; | 
					
						
							|  |  |  | using Mercados.Infrastructure.DataFetchers; | 
					
						
							|  |  |  | using Mercados.Infrastructure.Persistence; | 
					
						
							|  |  |  | using Mercados.Infrastructure.Persistence.Repositories; | 
					
						
							| 
									
										
										
										
											2025-07-01 10:46:13 -03:00
										 |  |  | using Mercados.Worker; | 
					
						
							| 
									
										
										
										
											2025-07-03 12:11:08 -03:00
										 |  |  | using Polly; | 
					
						
							|  |  |  | using Polly.Extensions.Http; | 
					
						
							| 
									
										
										
										
											2025-07-01 10:46:13 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-03 11:44:10 -03:00
										 |  |  | // Carga las variables de entorno desde el archivo .env en la raíz de la solución. | 
					
						
							|  |  |  | DotNetEnv.Env.Load(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 12:27:28 -03:00
										 |  |  | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  | // --- Configuración del Host --- | 
					
						
							|  |  |  | // Esto prepara el host del servicio, permitiendo la inyección de dependencias, | 
					
						
							|  |  |  | // la configuración desde appsettings.json y el logging. | 
					
						
							|  |  |  | IHost host = Host.CreateDefaultBuilder(args) | 
					
						
							|  |  |  |     .ConfigureServices((hostContext, services) => | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Obtenemos la configuración desde el host builder para usarla aquí. | 
					
						
							|  |  |  |         IConfiguration configuration = hostContext.Configuration; | 
					
						
							| 
									
										
										
										
											2025-07-01 10:46:13 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  |         // --- 1. Registro de Servicios de Infraestructura --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Registramos la fábrica de conexiones a la BD. Es un Singleton porque | 
					
						
							|  |  |  |         // solo necesita ser creada una vez para leer la cadena de conexión. | 
					
						
							|  |  |  |         services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Registramos los repositorios. Se crean "por petición" (Scoped). | 
					
						
							|  |  |  |         // En un worker, "Scoped" significa que se creará una instancia por cada | 
					
						
							|  |  |  |         // ejecución del servicio, lo cual es seguro y eficiente. | 
					
						
							|  |  |  |         services.AddScoped<ICotizacionGanadoRepository, CotizacionGanadoRepository>(); | 
					
						
							|  |  |  |         services.AddScoped<ICotizacionGranoRepository, CotizacionGranoRepository>(); | 
					
						
							|  |  |  |         services.AddScoped<ICotizacionBolsaRepository, CotizacionBolsaRepository>(); | 
					
						
							|  |  |  |         services.AddScoped<IFuenteDatoRepository, FuenteDatoRepository>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // --- 2. Registro de los Data Fetchers --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Registramos CADA uno de nuestros fetchers. El contenedor de DI sabrá | 
					
						
							|  |  |  |         // que todos implementan la interfaz IDataFetcher. | 
					
						
							|  |  |  |         services.AddScoped<IDataFetcher, MercadoAgroFetcher>(); | 
					
						
							|  |  |  |         services.AddScoped<IDataFetcher, BcrDataFetcher>(); | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |         services.AddScoped<IDataFetcher, FinnhubDataFetcher>(); | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  |         services.AddScoped<IDataFetcher, YahooFinanceDataFetcher>(); | 
					
						
							| 
									
										
										
										
											2025-07-03 12:11:08 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  |         // El cliente HTTP es fundamental para hacer llamadas a APIs externas. | 
					
						
							|  |  |  |         // Le damos un nombre al cliente de Finnhub para cumplir con los requisitos de su constructor. | 
					
						
							| 
									
										
										
										
											2025-07-03 12:11:08 -03:00
										 |  |  |         //services.AddHttpClient("Finnhub"); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |          // Configuramos CADA cliente HTTP que nuestros fetchers usan. | 
					
						
							|  |  |  |         // IHttpClientFactory nos permite nombrar y configurar clientes de forma independiente. | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Cliente para el scraper del MercadoAgro, con una política de reintentos | 
					
						
							|  |  |  |         services.AddHttpClient("MercadoAgroFetcher") | 
					
						
							|  |  |  |             .AddPolicyHandler(GetRetryPolicy()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Cliente para la API de BCR, con la misma política de reintentos | 
					
						
							|  |  |  |         services.AddHttpClient("BcrDataFetcher") | 
					
						
							|  |  |  |             .AddPolicyHandler(GetRetryPolicy()); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         // Cliente para Finnhub, con la misma política de reintentos | 
					
						
							|  |  |  |         services.AddHttpClient("FinnhubDataFetcher") | 
					
						
							|  |  |  |             .AddPolicyHandler(GetRetryPolicy()); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         // Cliente para YahooFinance (aunque es menos probable que falle, es buena práctica incluirlo) | 
					
						
							|  |  |  |         // La librería YahooFinanceApi usa su propio HttpClient, así que esta configuración | 
					
						
							|  |  |  |         // no le afectará directamente. La resiliencia para YahooFinance la manejaremos de otra forma si es necesario. | 
					
						
							|  |  |  |         // Por ahora, lo dejamos así y nos enfocamos en los que usan IHttpClientFactory. | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // --- 3. Registro del Worker Principal --- | 
					
						
							| 
									
										
										
										
											2025-07-03 12:11:08 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  |         // Finalmente, registramos nuestro servicio de fondo (el worker en sí). | 
					
						
							|  |  |  |         services.AddHostedService<DataFetchingService>(); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .Build(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-03 12:11:08 -03:00
										 |  |  | // Esta función define nuestra política de reintentos. | 
					
						
							|  |  |  | static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Polly.Extensions.Http nos da este método conveniente. | 
					
						
							|  |  |  |     return HttpPolicyExtensions | 
					
						
							|  |  |  |         // Maneja errores de red transitorios O códigos de estado de servidor que indican un problema temporal. | 
					
						
							|  |  |  |         .HandleTransientHttpError()  | 
					
						
							|  |  |  |         // También maneja el error 408 Request Timeout | 
					
						
							|  |  |  |         .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.RequestTimeout)  | 
					
						
							|  |  |  |         // Política de reintento con espera exponencial: 3 reintentos, esperando 2^intento segundos. | 
					
						
							|  |  |  |         .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),  | 
					
						
							|  |  |  |             onRetry: (outcome, timespan, retryAttempt, context) => | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // Registramos un log cada vez que se realiza un reintento. | 
					
						
							|  |  |  |                 // Esta es una forma de hacerlo sin tener acceso directo al ILogger aquí. | 
					
						
							|  |  |  |                 Console.WriteLine($"[Polly] Reintentando petición... Intento {retryAttempt}. Esperando {timespan.TotalSeconds}s. Causa: {outcome.Exception?.Message ?? outcome.Result.ReasonPhrase}"); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 12:19:00 -03:00
										 |  |  | await host.RunAsync(); |