How can I start a custom made service class from startup.cs in ASP.NET Core that needs dependency injection?
The service I use is a Telegram Message service that uses a few websockets with incoming data.
My service:
public class TelegramService
{
private IUserRepository _userRepository;
private IAsyncRepository<Coin> _coinRepository;
public TelegramService(IUserRepository userRepository, ICoinRepository coinRepository)
{
_userRepository = userRepository;
_coinRepository = coinRepository;
this.Start();
}
public async Task Start()
{
List<User> users = await _userRepository.GetAllUsers();
List<Coin> coins = new List<Coin>(await _coinRepository.ListAllAsync());
foreach (var coin in coins)
{
TelegramMessageService telegramMessageService = new TelegramMessageService(users, coin.Type);
}
}
My startup class. I did services.AddSingleton(); but obviously I’m missing something.
public void ConfigureServices(IServiceCollection services)
{
AddSwagger(services);
services.AddApplicationServices();
services.AddInfrastructureServices(Configuration);
services.AddDbContext<CryptoGuruDbContext>(options => options.UseSqlServer(configuration.GetConnectionString("CryptoGuruConnectionString"),
b => b.MigrationsAssembly(typeof(CryptoGuruDbContext).Assembly.FullName)));
services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<CryptoGuruDbContext>().AddDefaultTokenProviders();
services.AddScoped(typeof(IAsyncRepository<>), typeof(BaseRepository<>));
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<ICoinRepository, CoinRepository>();
services.AddControllers();
services.AddCors(options =>
{
options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
// Here is my service
services.AddSingleton<TelegramService>();
//services.AddTransient<TelegramService>();
//services.AddScoped<TelegramService>();
}
I tried AddTrasient
and AddScoped
. Both do not fire the Start() method from the constructor.
AddSingleton just gives an error:
System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: crypto_guru.Application.Services.TelegramService Lifetime: Singleton ImplementationType: crypto_guru.Application.Services.TelegramService': Cannot consume scoped service 'crypto_guru.Application.Contracts.Persistence.IUserRepository' from singleton 'crypto_guru.Application.Services.TelegramService'.)
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, IServiceProviderEngine engine, 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 crypto_guru.Api.Program.<Main>d__0.MoveNext() in D:CloudsOneDriveDocumentenGitHubcrypto-gurubackendX-Copter.ApiProgram.cs:line 27
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: crypto_guru.Application.Services.TelegramService Lifetime: Singleton ImplementationType: crypto_guru.Application.Services.TelegramService': Cannot consume scoped service 'crypto_guru.Application.Contracts.Persistence.IUserRepository' from singleton 'crypto_guru.Application.Services.TelegramService'.
Inner Exception 2:
InvalidOperationException: Cannot consume scoped service 'crypto_guru.Application.Contracts.Persistence.IUserRepository' from singleton 'crypto_guru.Application.Services.TelegramService'.
Sorry if this is a noob question, but I really can’t find the answer to do something like this.
3
Answers
The problem is that you have a lifetime mismatch between TelegramService and IUserRepository. TelegramService is a singleton and IUserRepository is scoped. If you try to inject a scoped service into a singleton then the scoped service essentially becomes a singleton. This is an example of a captive dependency. You should make TelegramService scoped as well.
You can’t use a service with a smaller lifetime. Scoped services only exist per-request, while singleton services are created once and the instance is shared.
try this
And I don’t think that it is a good idea to run async methods from the constructor. Try to make your start method just void. All your async calls convert to sync. You can find some workaround and keep async calls but the result still will be the same. So it is better just to use the simpliest way and make evertything sync.
The reason it is throwing this exception is because you are creating a singleton instance of TelegramService –
services.AddSingleton<TelegramService>();
and inside it’s constructor you’ve injectedIUserRepository
andICoinRepository
which are registered as scoped.Use either
or
Explanation-
Consider
TService
as yourIUserRepository/ICoinRepository
.Registering a service using
services.AddScoped<TService>();
will create a single instance ofTService
per request, means if you injectTService
in constructor of 3 different classes, then only 1 instance ofTService
will be created and will be used for all 3 injections during the scope of that request. Once the scope of that request is completed, theTService
instance will go out of scope, and new instance will be created for another request in the similar manner.Now consider
TService
as yourTelegramService
.Whereas
services.AddSingleton<TService>();
will create a single instace ofTService
for the lifetime of your application.Dry Run of application-
Now lets start your application, during the first request all 3 instances will be created and as soon as the request is completed the
IUserRepository
andICoinRepository
instances will go out of scope andTelegramService
instance (which is singleton) will hold no reference toIUserRepository/ICoinRepository
, this is a lifetime mismatch and that’s why the error.