Pattern for caching data in Asp.Net Core + EF Core?

asp.net-core c# entity-framework-core

Question

I have an Asp.Net Core + EF Core REST service. I created a DbContext class for a DB I want to call a SP on. The method pretty much looks like:

public IQueryable<xxx> Getxxxs()
{
    return Set<xxx>().FromSql("pr_Getxxx");
}

This all works, but there isn't any point in calling the SP every single time since the data the SP returns rarely changes. I'd like to make the data stale, say every 24 hours.

Is there a preferred pattern for doing that in Core? I see they have the .AddCaching extension method, but that seems like it would get injected into the controller? So its the controllers job to cache? I assume its thread-safe so I don't need to do any locking or anything like that? Seems like a race condition, if one thread is checking if the item is loaded into the cache, the other may be inserting it, etc?

1
6
11/14/2016 9:17:57 PM

Popular Answer

Well, you can apply the decorator pattern. It's nothing .NET Core specific, just a common pattern.

public class MyModel
{
    public string SomeValue { get; set; }
}

public interface IMyRepository
{
    IEnumerable<MyModel> GetModel();
}

public class MyRepository : IMyRepository
{
    public IEnumerable<MyModel> GetModel()
    {
        return Set<MyModel>().FromSql("pr_GetMyModel");
    }
}

public class CachedMyRepositoryDecorator : IMyRepository
{
    private readonly IMyRepository repository;
    private readonly IMemoryCache cache;
    private const string MyModelCacheKey = "myModelCacheKey";
    private MemoryCacheEntryOptions cacheOptions;

    // alternatively use IDistributedCache if you use redis and multiple services
    public CachedMyRepositoryDecorator(IMyRepository repository, IMemoryCache cache)
    {
        this.repository = repository;
        this.cache = cache;

        // 1 day caching
        cacheOptions = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(relative: TimeSpan.FromDays(1));
    }

    public IEnumerable<MyModel> GetModel()
    {
        // Check cache
        var value = cache.Get<IEnumerable<MyModel>>("myModelCacheKey");
        if(value==null)
        {
            // Not found, get from DB
            value = repository.GetModel();

            // write it to the cache
            cache.Set("myModelCacheKey", value, cacheOptions);
        }

        return value;
    }
}

Since the ASP.NET Core DI doesn't support interceptors or decorators, your DI registration will become a bit more verbose. Alternatively use a 3rd party IoC container which supports decorator registrations.

services.AddScoped<MyRepository>();
services.AddScoped<IMyRepository, CachedMyRepositoryDecorator>(
    provider => new CachedMyRepositoryDecorator(
        provider.GetService<MyRepository>(),
        provider.GetService<IMemoryCache>()
    ));

This has the advantage that you have a clear separation of concerns and can easily disable the caching by changing the DI configuration to

services.AddScoped<IMyRepository,MyRepository>();
6
7/9/2019 1:03:44 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow