realist
realist

Reputation: 2375

Injecting scoped service from singleton service in .net core 2.1

I have a cacheHelper. When I add the dependency of CacheHelper to startup with "AddScoped", it is working. But,CacheHelper.cs is running for every request. So, I convert to "AddSingleton" like below. But I'm taking an error, like that: Cannot consume scoped service 'MyProject.DataAccess.IUnitOfWork' from singleton 'MyProject.Caching.ICacheHelper' How can I fix this problem?

Strartup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
        services.AddScoped<IUnitOfWork, UnitOfWork>();
        services.AddScoped<IJwtHelper, JwtHelper>();
        services.AddScoped<IAuditHelper, AuditHelper>();
        services.TryAdd(ServiceDescriptor.Singleton<IMemoryCache, MemoryCache>());
        services.AddSingleton<ICacheHelper, CacheHelper>();
        services.AddMvc();

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

CacheHelper.cs

public class CacheHelper : ICacheHelper
{
    private readonly IUnitOfWork unitOfWork;
    public IMemoryCache Cache { get; }

    public CacheHelper(IUnitOfWork unitOfWork, IMemoryCache cache)
    {
        this.unitOfWork = unitOfWork;
        Cache = cache;
    }

    public void SetCommonCacheItems()
    {
        var cities = unitOfWork.CityRepo.GetAll();
        Cache.Set("cities", cities);

        string obj;
        Cache.TryGetValue<string>("cities", out obj);
    }

    public string GetCities()
    {
        string obj;
        Cache.TryGetValue<string>("cities", out obj);

        return obj;
    }
}

Upvotes: 2

Views: 2787

Answers (1)

Tseng
Tseng

Reputation: 64150

The way you had it before was the correct one. ICacheHelper should and have to be scoped.

Just your cache implementation is wrong. Get cities will be called, it checks the cache. If not found, it will get the data and put it into the cache.

public class CacheHelper : ICacheHelper
{
    private readonly IUnitOfWork unitOfWork;
    public IMemoryCache Cache { get; }

    public CacheHelper(IUnitOfWork unitOfWork, IMemoryCache cache)
    {
        this.unitOfWork = unitOfWork;
        Cache = cache;
    }

    public string GetCities()
    {
        if(!Cache.TryGetValue<string>("cities", string out cities))
        {
            // not found in cache, obtain it
            cities = unitOfWork.CityRepo.GetAll();
            Cache.Set("cities", cities);
        }

        return cities;
    }
}

You don't need SetCommonCacheItems() method. The important thing is that IMemoryCache is static, since it will contain the data. UoW has to be scoped, because of the database, otherwise you would have memory leaks (especially when using EF Core, since its caching/tracking the entities).

Upvotes: 2

Related Questions