skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. 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

     services.AddScoped<TelegramService>();
    //or you can try
     services.AddTransient<TelegramService>();
    
    

    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.

    Login or Signup to reply.
  3. 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 injected IUserRepository and ICoinRepository which are registered as scoped.

    Use either

    services.AddScoped<IUserRepository>();
    services.AddScoped<ICoinRepository>();
    services.AddScoped<TelegramService>();
    

    or

    services.AddSingleton<IUserRepository>();
    services.AddSingleton<ICoinRepository>();
    services.AddSingleton<TelegramService>();
    

    Explanation-
    Consider TService as your IUserRepository/ICoinRepository.
    Registering a service using services.AddScoped<TService>(); will create a single instance of TService per request, means if you inject TService in constructor of 3 different classes, then only 1 instance of TService 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, the TService instance will go out of scope, and new instance will be created for another request in the similar manner.
    Now consider TService as your TelegramService.
    Whereas services.AddSingleton<TService>(); will create a single instace of TService 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 and ICoinRepository instances will go out of scope and TelegramService instance (which is singleton) will hold no reference to IUserRepository/ICoinRepository, this is a lifetime mismatch and that’s why the error.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search