0

In my asp.net core 6 mvc project, I have a transient service ContentService. This service is inherited from IContentService and added to Program.cs using

builder.Services.AddTransient<IContentService, ContentService>();

Here is the implementation of the service:

public interface IContentService
{
    public string GetText(int id);
}

public class ContentService : IContentService
{
    private readonly ContentDbContext _context;
    private List<string> textList;

    public ContentService(ContentDbContext context)
    {
        _context = context;

        ReadTexts();
    }

    public void ReadTexts()
    {
        // Reads text from the database and fills the textList.
        // THIS CODE MUST RUN ONLY FOR THE FIRST REQUEST.
    }

    public string GetText(int id)
    {
        return textList(id);
    }
}

The problem is every time ContentService is requested, the constructor runs and ReadTexts() is called, which is a heavy function that must only run for the first request. Is there anyway to implement a transient service with a code block that runs only for the first request? Note that I cannot use AddSingleton.

Jaber
  • 17
  • 6
  • any specific reason why can't you use AddSingleton. it is for such purpose only. – CodingMytra Jul 06 '22 at 14:26
  • @CodingMytra Occasionally I need to edit some text within the database, and if I want to use AddSingleton, the database also needs to be defined as Singleton. In this situation, I run into [this problem](https://stackoverflow.com/questions/48202403). – Jaber Jul 06 '22 at 14:44
  • Seems conceptionally wrong to put a heavy load function into the constructor to be honest. If you really need to do something like this, a more DI-way might be to use in-memory cache to keep a state that the function has run. Or separate the "loading" into a warmup function that is only running once when the application starts - depends on you use-case... – Mario Jul 06 '22 at 15:09
  • @Mario could you explain more about "separate the loading into a warmup function"? – Jaber Jul 06 '22 at 17:25
  • 1
    @Jaber I used the following code in an adjusted way in our projects https://github.com/thomaslevesque/AspNetCore.AsyncInitialization/tree/master/src/AspNetCore.AsyncInitialization you can call `await host.InitAsync();` and have as many initializer as you need. The pipeline starts accepting requests only afterwards which is nice. But it depends if thats what you are searching. – Mario Jul 08 '22 at 07:55

1 Answers1

0

You have to make textList as static if you want it to hold data which should be read only on first request.

try this.

public class ContentService : IContentService
{
    private readonly ContentDbContext _context;
    private static List<string> textList = new List<string>();

    public ContentService(ContentDbContext context)
    {
        _context = context;

        ReadTexts();
    }

    public void ReadTexts()
    {
       if(!textList.Any())
       {
        // Reads text from the database and fills the textList.
        // THIS CODE MUST RUN ONLY FOR THE FIRST REQUEST.
       }
    }

    public string GetText(int id)
    {
        return textList(id);
    }
}
CodingMytra
  • 2,234
  • 1
  • 9
  • 21