Home > Net >  How cache data from database and use it in Scoped class ASP .NET Core
How cache data from database and use it in Scoped class ASP .NET Core

Time:09-24

I would like to cache date from one table, and return it when user do request to this table. I created class like this:

public interface ICategoryCache
{
    IEnumerable<Category> GetCategories();
}
public class CategoryCache : ICategoryCache
{
    private IEnumerable<Category> _categories;
    public CategoryCache(ItBuildsDbContext context)
    {
        _categories = context.Category.ToList();
    }

    public IEnumerable<Category> GetCategories()
    {
        return _categories;
    }
}

I wanted to add dependency injection as Singleton, but class which have to use this object is Scoped (and it throw error: Cannot consume scoped service). How should I do it properly? I am not able to change Scoped class to Singleton.

Should I for example create Factory which will create my Singleton object CategoryCache?

My solution for this problem which work:

public class CategoryCache
{
    private readonly IEnumerable<Category> _categories;
    private static CategoryCache? _categoryCache;

    private CategoryCache(ItBuildsDbContext context)
    {
        _categories = context.Category.ToList();
    }

    public static CategoryCache Create(ItBuildsDbContext context)
    {
        if(_categoryCache == null)
        {
            _categoryCache = new CategoryCache(context);
        }

        return _categoryCache;
    }

    public IEnumerable<Category> GetCategories()
    {
        return _categories!;
    }
}

public interface IFactoryCategoryCache
{
    CategoryCache Create();
}

public class FactoryCategoryCache : IFactoryCategoryCache
{
    private readonly ItBuildsDbContext _context;
    public FactoryCategoryCache(ItBuildsDbContext context)
    {
        _context = context;
    }

    public CategoryCache Create()
    {
        return CategoryCache.Create(_context);
    }
}


service.AddScoped<IFactoryCategoryCache, FactoryCategoryCache>();

But is there a better solution here?

CodePudding user response:

You are maybe doing something wrong, because it is possible to inject a singleton in a scoped service.

Example:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ICategoryCache>(new CategoryCache(new List<string> { "Category 1", "Category 2", "Category 3" }));
builder.Services.AddScoped<ScopedTestService>();
var app = builder.Build();
    public interface ICategoryCache
    {
        IEnumerable<string> GetCategories();
    }

    public class CategoryCache : ICategoryCache
    {
        private IEnumerable<string> _categories;
        public CategoryCache(IEnumerable<string> context)
        {
            _categories = context;
        }

        public IEnumerable<string> GetCategories()
        {
            return _categories;
        }
    }
public class ScopedTestService
{
    private readonly ICategoryCache _categoryCache;

    public ScopedTestService(ICategoryCache categoryCache)
    {
        _categoryCache = categoryCache;
    }

    public IEnumerable<string> GetCategories()
    {
        return _categoryCache.GetCategories();
    }
}

CodePudding user response:

You are getting the error because the default lifetime of the dbcontext is Scoped,and you injected it to the Singleton Service

enter image description here

If you just cache date from one table and don't modify the data at any time,you could set as below:

services.AddDbContext<ServicelifetimeContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString(".....")), ServiceLifetime.Singleton);

Update:

You may try with DbContextFactory for your requirement

in startup:

services.AddDbContextFactory<ServicelifetimeContext>(options =>
               options.UseSqlServer(Configuration.GetConnectionString("ServicelifetimeContext")));

in different services:

public CategoryCache(IDbContextFactory<ServicelifetimeContext> contextFactory)
        {
            _mymodels = contextFactory.CreateDbContext().Mymodel.ToList();
        }

I tried in a MVC project:

public MymodelsController(IDbContextFactory<ServicelifetimeContext> contextFactory,  ICategoryCache categoryCache)
        {
            _context = contextFactory.CreateDbContext();
            _categoryCache = categoryCache;
        }

        // GET: Mymodels
        public IActionResult Index()
        {
            return View(_categoryCache.GetMymodels());
        }
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var mymodel = await _context.Mymodel.FindAsync(id);
            if (mymodel == null)
            {
                return NotFound();
            }
            return View(mymodel);
        }

Result:

enter image description here

Now you could get the context with different lifetime

  • Related