In my ASP.NET Core-6 Web API application, I want to get data from third party API and save into the database using HangFire.
I have these code.
In appsettings.json I have:
"Endpoints": {
"accountBalanceUrl": "https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail"
}
Then JSON Result is shown below:
{
"AccountNumber": "string",
"AccountName": "string",
"CurrentBalance": 0,
"AvailableBalance": 0,
"Currency": "string"
}
So far, I have done this:
BalanceEnquiryResponseDto:
public class BalanceEnquiryResponseDto
{
public List<BalanceListDto> AccountBalances
{
get;
set;
}
}
BalanceListDto:
public class BalanceListDto
{
public string AccountNumber
{
get;
set;
}
public string AccountName
{
get;
set;
}
public decimal CurrentBalance
{
get;
set;
}
public decimal AvailableBalance
{
get;
set;
}
public string Currency
{
get;
set;
}
}
BaseResponse:
public class BaseResponse
{
public bool Success { get; set; } = true;
public string Message { get; set; }
}
Interface:
public interface IAccountBalanceService
{
Task<BaseResponse> CreateAccountBalanceAsync();
}
Implementation:
public class AccountBalanceService : IAccountBalanceService
{
private readonly IMapper _mapper;
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger _logger;
private readonly IConfiguration _config;
private readonly HttpClient _myClient;
public AccountBalanceService(
IUnitOfWork unitOfWork,
ILogger logger,
IMapper mapper,
HttpClient myClient,
IConfiguration config
)
{
_mapper = mapper;
_unitOfWork = unitOfWork;
_logger = logger;
_myClient = myClient;
_config = config;
}
public async Task<BaseResponse> CreateAccountBalanceAsync()
{
var balanceResponse = new BaseResponse();
try
{
string accountBalanceUrl = _config.GetSection("Endpoints").GetValue<string>("accountBalanceUrl");
HttpResponseMessage response = _myClient.GetAsync(bankBranchUrl).Result;
var stringResult = response.Content.ReadAsStringAsync().Result;
BankBranchResponse list = JsonConvert.DeserializeObject<BalanceEnquiryResponseDto>(stringResult);
foreach (var singleBalance in list.AccountBalances)
{
CustomerBalance res = new CustomerBalance();
//set all fields here
res.AccountNumber = singleBalance.AccountNumber;
res.AccountName = singleBalance.AccountName;
res.CurrentBalance = singleBalance.CurrentBalance;
res.AvailableBalance = singleBalance.AvailableBalance;
res.Currency = singleBalance.Currency;
await _unitOfWork.CustomerBalances.InsertAsync(res);
}
await _unitOfWork.Save();
}
catch (Exception ex)
{
_logger.Error("An Error occured " ex.ToString());
}
return balanceResponse;
}
}
This is the original url for the third party API:
https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail
For the CronJob and Schedule, I am using HangFire.
HangFireServiceExtension:
public static class HangFireServiceExtension
{
public static void AddHangFireConfigurations(this IServiceCollection services, IConfiguration config)
{
string connStr;
connStr = config.GetConnectionString("DefaultConnection");
services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(connStr, new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
// Add the processing server as IHostedService
services.AddHangfireServer();
}
}
Program.cs:
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
var environment = builder.Environment;
builder.Services.AddHangFireConfigurations(configuration);
builder.Services.AddScope<IAccountBalanceService, AccountBalanceService>();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseHangfireDashboard();
RecurringJob.AddOrUpdate<IAccountBalanceService>("Post_All_Balances", service =>
service.CreateAccountBalanceAsync(),
Cron.Daily(2, 0), TimeZoneInfo.Local);
app.MapControllers();
app.Run();
I didn't create any Controller for it, since I am running it as a backgroung cron job.
It is schedule to save the data daily by 2:am.
I was able to build it successfully. But when I tried to run it in Visual Studio using IIS Express, I got this error:
System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: IAccountBalanceService Lifetime: Scoped ImplementationType: AccountBalanceService': Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'AccountBalanceService'.)
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()
at Program.<<Main>$>d__0.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Program.<Main>(String[] args)
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: IAccountBalanceService Lifetime: Scoped ImplementationType: AccountBalanceService': Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'AccountBalanceService'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'AccountBalanceService'.
How do I resolve this?
Thanks
CodePudding user response:
You need to register IHttpClientFactory
/HttpClient
in the DI to use it. For example via typed client:
builder.Services.AddHttpClient<IAccountBalanceService, AccountBalanceService>(); // instead of builder.Services.AddScope<IAccountBalanceService, AccountBalanceService>();
Note that:
The typed client is registered as transient with DI container
If you are required to have the IAccountBalanceService
to be scoped you can keep current registration and resolve IHttpClientFactory
:
builder.Services.AddHttpClient();
builder.Services.AddScope<IAccountBalanceService, AccountBalanceService>();
and in the service ctor:
public AccountBalanceService(
IUnitOfWork unitOfWork,
ILogger logger,
IMapper mapper,
IHttpClientFactory factory,
IConfiguration config
)
{
_mapper = mapper;
_unitOfWork = unitOfWork;
_logger = logger;
_myClient = factory.CreateClient();
_config = config;
}