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

Sle*_*mer 4 c# entity-framework-core asp.net-core

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");
}
Run Code Online (Sandbox Code Playgroud)

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?

ham*_*bab 6

你可以使用这个第三方包:https : //github.com/VahidN/EFSecondLevelCache.Core

使用 AspNetCore MW 和 EfCore 扩展(可缓存),如下所示:

var posts = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable()
                   .ProjectTo<PostDto>(configuration: _mapper.ConfigurationProvider)
                   .ToList();
Run Code Online (Sandbox Code Playgroud)

ProjectTo<>() 是 AutoMapper 扩展。


Tse*_*eng 5

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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>()
    ));
Run Code Online (Sandbox Code Playgroud)

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>();
Run Code Online (Sandbox Code Playgroud)

  • 我最近在此处创建了一个演示此方法的示例:https://github.com/ardalis/cachedrepository/ (2认同)